{ "info": { "author": "Guillaume Subiron", "author_email": "maethor+pip@subiron.org", "bugtrack_url": null, "classifiers": [ "Intended Audience :: Developers", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3.3" ], "description": "# LDAPOM Model\n\nThis package provides some classes to build a Service layer and expose an API that interacts with the model. The first idea is to remove all logic of the routes and model of the Flask application, and put it in the service layer. The second goal is to provide a common API that can be use to manipulate a model regardless of its storage backend.\n\n## Sorry\u2026 What?\n\nA common Flask organisation consists in separating model (SQLAlchemy classes, for instance), and routes. When the application grows, you can also use Blueprints. One Blueprint by model for instance :\n\n```\napp/ # Flask main application\n __init__.py\n customer/ # Customer Blueprint\n __init__.py\n forms.py # User forms\n models.py # User model\n views.py # User views\n templates/ # User templates\n ...\n product/ # Product Blueprint\n __init__.py\n forms.py\n models.py\n views.py\n templates/\n ...\n templates/ # Common templates\n layout.html\n macros.html\n config.py\n filters.py\n ...\n```\n\nThat's great. But where do you put the logic ? \"Yeah\u2026 It depends\u2026\". \"Sometimes in the model, sometimes in the views\u2026\". You see what I mean, right ? Problem is, as the application grows, it became impossible to maintain, because you just doesn't know where the logic is done.\n\nFirst, \"model layer\" is supposed to be only data storage manipulation. But putting the logic in the \"view layer\" really is bad idea. What if you want to add a REST API to you application ? You just copy paste all the code from your classic route to make the new API routes ?\n\nFinally, if you want to replace SQL database by LDAP, you should be able to do it without modifying anything but the model. So you can't put any logic in \"models.py\". So, please, stop using SQLAlchemy methods in your views.\n\nThis package gives you a solution.\n\n## Installation\n\n pip install flask-servicelayer\n\n## Example\n\nThe package provides two main classes, one to build a Flask SQLAlchemy-based service, and one to build a LDAPom-model-based service.\n\n### SQLAlchemyService\n\nSee how the route became clean when all the logic and the SQLAlchemy specifics code are put in a service layer.\n\n`app/product/services.py` :\n\n```python\nfrom flask.ext.servicelayer import SQLAlchemyService\n\nfrom .models import Product\nfrom .. import db\n\nclass ProductService(SQLAlchemyService):\n __model__ = Product\n __db__ = db\n```\n\n`app/product/views.py`:\n\n```python\nproducts = ProductService()\n\n@product.route(\"/\")\ndef index():\n return render_template('product/list.html', products=products.all())\n\n@product.route(\"/new\", methods=['GET', 'POST'])\ndef new():\n form = ProductForm()\n if form.validate_on_submit():\n products.create(**{field.name: field.data for field in form})\n return redirect(url_for('.index'))\n return render_template('product/new.html', form=form)\n\n@product.route(\"/delete/\")\ndef delete(id):\n products.delete(products.get_or_404(id))\n return redirect(url_for('.index'))\n```\n\n### LDAPOMService\n\nAnd see how customer views are really looking like product views, even if the Customer model is based on [LDAPOM-model](https://github.com/sysnove/ldapom-model). Do you see any complex retrieve functions in the views?\n\n`app/customer/services.py`:\n\n```python\nfrom flask.ext.servicelayer import LDAPOMService\n\nfrom .. import ldap\nfrom .models import Customer\n\nclass CustomerService(LDAPOMService):\n __model__ = Customer\n __ldap__ = ldap\n```\n\n`app/customer/views.py`:\n\n```python\ncustomers = CustomerService()\n\n@customer.route(\"/\")\ndef index():\n return render_template('customer/list.html', customers=customers.all())\n\n@customer.show(/)\ndef show(id):\n customer = customers.get_or_404(id)\n return render_template(\"customer/show.html\", customer=customer)\n```\n\n## Tips\n\n### Instantiation\n\nDepending on how you have structure the rest of your application, you can instantiate a service object when you need it, or instantiate on object of each service at the beginning of each request, and store it in `g` to use it everywhere. Personally, I like to instantiate each service in the `before_request` method of its Blueprint. For instance, in `app/customer/__init__.py`:\n\n```python\nfrom flask import Blueprint, g\n\ndomain = Blueprint(\"customer\", __name__, template_folder='templates')\n\nfrom .services import CustomerService\n\n@customer.before_app_request\ndef before_request():\n if 'ldap' in g:\n g.customers = CustomerService(g.ldap)\n\nfrom . import views\n```\n\n### LDAP Cache\n\nThe package provides an `LDAPOMCachedService` class. This class inherits `LDAPOMService` and can be use exactly the same way. The only difference is that `all()`, `get()` and `find()` methods are cached within the service object to avoid doing a new LDAP request every time. This is very powerful when your service object is put in the global var `g` to be used everywhere during your request.\n\n## Licence\n\nThis code is under [WTFPL](https://en.wikipedia.org/wiki/WTFPL). Just do what the fuck you want with it.\n\nThe idea is based on [Matt Wright's article How do I Structure My Flask Application](http://mattupstate.com/python/2013/06/26/how-i-structure-my-flask-applications.html#s2c). Matt, if you read this, thank you!", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://github.com/sysnove/flask-servicelayer", "keywords": null, "license": "WTFPL", "maintainer": null, "maintainer_email": null, "name": "Flask-ServiceLayer", "package_url": "https://pypi.org/project/Flask-ServiceLayer/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/Flask-ServiceLayer/", "project_urls": { "Download": "UNKNOWN", "Homepage": "http://github.com/sysnove/flask-servicelayer" }, "release_url": "https://pypi.org/project/Flask-ServiceLayer/0.0.4/", "requires_dist": null, "requires_python": null, "summary": "Base classes to create a Service Layer in Flask.", "version": "0.0.4" }, "last_serial": 1891171, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "4d580452ca738f89791561bcdcea9eaa", "sha256": "828216a4e8b88be517eb2ae2f7ffb9b604fd6242aa09fdba54650c634f22917d" }, "downloads": -1, "filename": "Flask-ServiceLayer-0.0.1.tar.gz", "has_sig": false, "md5_digest": "4d580452ca738f89791561bcdcea9eaa", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7293, "upload_time": "2014-06-04T16:31:29", "url": "https://files.pythonhosted.org/packages/73/8d/4d0b12281e6ebd58273c546a8d6b4ab665679710977d3d88632190b24695/Flask-ServiceLayer-0.0.1.tar.gz" } ], "0.0.2": [ { "comment_text": "", "digests": { "md5": "99e214e80688090fd8c4de1f99055028", "sha256": "2b60b6e9cac84d92e77176959946304697c177b4c04240566557e06ff60d0859" }, "downloads": -1, "filename": "Flask-ServiceLayer-0.0.2.tar.gz", "has_sig": false, "md5_digest": "99e214e80688090fd8c4de1f99055028", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7285, "upload_time": "2014-07-10T12:03:35", "url": "https://files.pythonhosted.org/packages/5d/f5/2806baa6bcab1888019666c65644e4879780a835541fc7035542c9818411/Flask-ServiceLayer-0.0.2.tar.gz" } ], "0.0.3": [ { "comment_text": "", "digests": { "md5": "82c391cd13645e3bdf263fa59e57f5d3", "sha256": "7d7eeccb9cb4ca65b8645374488af4187bfb9f10d20b7e4fc98fe9b9d4e29f24" }, "downloads": -1, "filename": "Flask-ServiceLayer-0.0.3.tar.gz", "has_sig": false, "md5_digest": "82c391cd13645e3bdf263fa59e57f5d3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7313, "upload_time": "2014-07-10T12:20:54", "url": "https://files.pythonhosted.org/packages/23/a2/a556d07d9f7ce73f67700fd4eca2a62e08008e2410004c12aca4ba2f30a6/Flask-ServiceLayer-0.0.3.tar.gz" } ], "0.0.4": [ { "comment_text": "", "digests": { "md5": "51cf4d3d38fe026cb2c6e42bc981433a", "sha256": "95e626a32d45bb13c961bd7b9d26a8c02a7a06f86bb9cfce32310ba48934f376" }, "downloads": -1, "filename": "Flask-ServiceLayer-0.0.4.tar.gz", "has_sig": false, "md5_digest": "51cf4d3d38fe026cb2c6e42bc981433a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7328, "upload_time": "2016-01-06T13:01:29", "url": "https://files.pythonhosted.org/packages/17/f8/8892d714f467bd5d5fd664ee9c1c7f38725e703fad6622636d54eab0f3c3/Flask-ServiceLayer-0.0.4.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "51cf4d3d38fe026cb2c6e42bc981433a", "sha256": "95e626a32d45bb13c961bd7b9d26a8c02a7a06f86bb9cfce32310ba48934f376" }, "downloads": -1, "filename": "Flask-ServiceLayer-0.0.4.tar.gz", "has_sig": false, "md5_digest": "51cf4d3d38fe026cb2c6e42bc981433a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7328, "upload_time": "2016-01-06T13:01:29", "url": "https://files.pythonhosted.org/packages/17/f8/8892d714f467bd5d5fd664ee9c1c7f38725e703fad6622636d54eab0f3c3/Flask-ServiceLayer-0.0.4.tar.gz" } ] }