{ "info": { "author": "left-join", "author_email": "left-join@riseup.net", "bugtrack_url": null, "classifiers": [ "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "# flask-restive\nFlask-RESTive is a REST API Flask extension based on [Flask-RESTful](https://github.com/flask-restful/flask-restful) & [Marshmallow](https://github.com/marshmallow-code/marshmallow).\n\n[![Build Status](https://travis-ci.org/left-join/flask-restive.svg?branch=master)](https://travis-ci.org/left-join/flask-restive)\n[![Coverage Status](https://coveralls.io/repos/github/left-join/flask-restive/badge.svg?branch=master)](https://coveralls.io/github/left-join/flask-restive?branch=master)\n[![Code Health](https://landscape.io/github/left-join/flask-restive/master/landscape.svg?style=flat)](https://landscape.io/github/left-join/flask-restive/master)\n[![PyPI Version](https://img.shields.io/pypi/v/Flask-RESTive.svg)](https://pypi.python.org/pypi/Flask-RESTive)\n\n\n## Installation\n```bash\npip install flask-restive\n```\n\n## Requirements\n- Python >= 2.7 or >= 3.4\n\n## Introdution\n\n#### Reusable resource concept\nIn many cases we don't need to duplicate resource's methods code.\nFlask-RESTive adheres to a declarative approach. All that we need it's just define serializer behaviour and repo behaviour. The resource code it is not a place for define any business logic, it's view and we use it just for call serializers, repo and results render.\n```python\nclass ClientResource(StorageResource):\n data_schema_cls = ClientSchema\n storage_cls = ClientStorage\n```\n\n#### Storage concept\nStorage is a repo class in DDD (Domain Driven Design) methodology. Storage can implement workflow with any database or multiple databases. Abstract storage provides interface methods:\n```python\ndef open(self):\n ...\n\ndef close(self, exception=None):\n ...\n\ndef get_item(self, filter_params, **kwargs):\n ...\n\ndef get_count(self, filter_params=None, **kwargs):\n ...\n\ndef get_list(self, filter_params=None, slice_params=None, sorting_params=None, **kwargs):\n ...\n\ndef create_item(self, data_params, **kwargs):\n ...\n\ndef create_list(self, data_params, **kwargs):\n ...\n\ndef update_item(self, data_params, **kwargs):\n ...\n\ndef update_list(self, data_params, **kwargs):\n ...\n\ndef delete_list(self, filter_params=None, **kwargs):\n ...\n```\nAnybody can make his own implementation of his special storage. Combine simple storage bricks to implement business logic layer in your storage.\nStorage supports **primary_key_fields** meta-attribute and use it to wrap result data to special object with primary_key property.\n```python\nclass ClientStorage(Storage):\n class Meta(Storage.Meta):\n primary_key_fields = ('id',)\n```\nWrapped objects are more useful to work with them on many storage combining and result processing.\n\n#### Schema concept\nSchema is a Marshmallow library class that implements serializer/deserializer logic. It's useful to define model fields in declarative style. It's a right to place to make any data validations or transmutations before or after storage data processing.\n```python\nclass ClientSchema(Schema):\n id = fields.Integer(required=True)\n first_name = fields.String(required=True)\n last_name = fields.String()\n```\nData schema supports **primary_key_fields**, **sortable_fields** and **default_sorting** meta-attributes. Filter schema and sorting schema use it to auto-make filter and sorting fields and validation rules.\n```python\nclass ClientSchema(Schema):\n id = fields.Integer(required=True)\n first_name = fields.String(required=True)\n last_name = fields.String()\n\n class Meta(Schema.Meta):\n sortable_fields = ('id', 'first_name', 'last_name')\n default_sorting = ('last_name', 'first_name', 'id')\n```\n\n## How to use\n\n```python\nfrom datetime import datetime\n\nfrom flask import Flask\nfrom flask_restive import Api, StorageResource, UUIDSchema, fields\nfrom marshmallow import pre_load\nfrom flask_restive_sqlalchemy import Model, Storage\nfrom sqlalchemy import Column, String, DateTime\nfrom sqlalchemy_utils import UUIDType\n\n\napp = Flask(__name__)\n\napp.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'\n\n\ndef utc_time():\n return datetime.utcnow().replace(microsecond=0)\n\n\nclass ClientSchema(UUIDSchema):\n first_name = fields.String(required=True)\n last_name = fields.String(required=True)\n created_on = fields.DateTime(\n required=True,\n missing=lambda: utc_time().isoformat())\n updated_on = fields.DateTime()\n\n class Meta(UUIDSchema.Meta):\n sortable_fields = ('id', 'created_on', 'updated_on')\n default_sorting = ('-updated_on', '-created_on', 'id')\n\n @pre_load(pass_many=False)\n def set_updated_on(self, data):\n # update time stamp on each create/update operation\n data['updated_on'] = utc_time().isoformat()\n return data\n\n\nclass ClientModel(Model):\n id = Column(UUIDType, primary_key=True)\n first_name = Column(String)\n last_name = Column(String)\n created_on = Column(DateTime)\n updated_on = Column(DateTime)\n\n\nclass ClientStorage(Storage):\n\n class Meta(Storage.Meta):\n model_cls = ClientModel\n primary_key_fields = ('id',)\n\n\nclass ClientResource(StorageResource):\n data_schema_cls = ClientSchema\n storage_cls = ClientStorage\n\n\napi = Api(app, prefix='/api/v1', api_resources=[\n (ClientResource, ('/clients', '/clients/')),\n])\n\n\nif __name__ == '__main__':\n app.run(host='0.0.0.0', port=5000)\n\n```\n\nLet's create new client:\n```bash\ncurl -X POST \"http://localhost:5000/api/v1/clients\" -H \"Content-Type: application/json\" -d '{\"first_name\": \"Alice\", \"last_name\": \"Liddell\"}'\n{\n \"id\": \"0372be43-a668-421e-b8df-7246cdb40857\",\n \"first_name\": \"Alice\", \"last_name\": \"Liddell\",\n \"created_on\": \"2017-09-08T20:44:37\",\n \"updated_on\": \"2017-09-08T20:44:37\"\n}\n```\n\nLet's create two more:\n```bash\ncurl -X POST \"http://localhost:5000/api/v1/clients\" -H \"Content-Type: application/json\" -d '[{\"first_name\": \"Mad\", \"last_name\": \"Hatter\"}, {\"first_name\": \"Cheshire\", \"last_name\": \"Cat\"}]'\n[\n {\n \"id\": \"a593f5e2-e588-4e2a-ae57-c4dd8a3faed5\",\n \"first_name\": \"Mad\",\n \"last_name\": \"Hatter\",\n \"created_on\": \"2017-09-08T20:45:15\",\n \"updated_on\": \"2017-09-08T20:45:15\"\n },\n {\n \"id\": \"c761ef71-d4b0-4b14-aa45-549ffcb72234\",\n \"first_name\": \"Cheshire\",\n \"last_name\": \"Cat\",\n \"created_on\": \"2017-09-08T20:45:15\",\n \"updated_on\": \"2017-09-08T20:45:15\"\n }\n]\n```\n\nLet's list created clients:\n```bash\ncurl -X GET \"http://localhost:5000/api/v1/clients\"\n{\n \"offset\": 0,\n \"limit\": null,\n \"total_count\": 3,\n \"items_count\": 3,\n \"items_list\": [\n {\n \"id\": \"a593f5e2-e588-4e2a-ae57-c4dd8a3faed5\",\n \"first_name\": \"Mad\",\n \"last_name\": \"Hatter\",\n \"created_on\": \"2017-09-08T20:45:15\",\n \"updated_on\": \"2017-09-08T20:45:15\"\n },\n {\n \"id\": \"c761ef71-d4b0-4b14-aa45-549ffcb72234\",\n \"first_name\": \"Cheshire\",\n \"last_name\": \"Cat\", \"created_on\": \"2017-09-08T20:45:15\",\n \"updated_on\": \"2017-09-08T20:45:15\"\n },\n {\n \"id\": \"0372be43-a668-421e-b8df-7246cdb40857\",\n \"first_name\": \"Alice\",\n \"last_name\": \"Liddell\",\n \"created_on\": \"2017-09-08T20:44:37\",\n \"updated_on\": \"2017-09-08T20:44:37\"\n }\n ]\n}\n```\n\nLet's take one client:\n```bash\ncurl -X GET \"http://localhost:5000/api/v1/clients/0372be43-a668-421e-b8df-7246cdb40857\"\n{\n \"id\": \"0372be43-a668-421e-b8df-7246cdb40857\",\n \"first_name\": \"Alice\",\n \"last_name\": \"Liddell\",\n \"created_on\": \"2017-09-08T20:44:37\",\n \"updated_on\": \"2017-09-08T20:44:37\"\n}\n```\n\nLet's paginate list of clients:\n```bash\ncurl -X GET \"http://localhost:5000/api/v1/clients?offset=2&limit=2\"\n{\n \"offset\": 2,\n \"limit\": 2,\n \"total_count\": 3,\n \"items_count\": 1,\n \"items_list\": [\n {\n \"id\": \"0372be43-a668-421e-b8df-7246cdb40857\",\n \"first_name\": \"Alice\",\n \"last_name\": \"Liddell\",\n \"created_on\": \"2017-09-08T20:44:37\",\n \"updated_on\": \"2017-09-08T20:44:37\"\n }\n ]\n}\n```\n\nLet's update one client:\n```bash\ncurl -X PATCH \"http://localhost:5000/api/v1/clients/0372be43-a668-421e-b8df-7246cdb40857\" -H \"Content-Type: application/json\" -d '{\"last_name\": \"Hatter\"}'\n{\n \"id\": \"0372be43-a668-421e-b8df-7246cdb40857\",\n \"first_name\": \"Alice\",\n \"last_name\": \"Hatter\",\n \"created_on\": \"2017-09-08T20:44:37\",\n \"updated_on\": \"2017-09-08T20:52:07\"\n}\n```\n\nLet's list clients again:\n```bash\ncurl -X GET \"http://localhost:5000/api/v1/clients\"\n{\n \"offset\": 0,\n \"limit\": null,\n \"total_count\": 3,\n \"items_count\": 3,\n \"items_list\": [\n {\n \"id\": \"0372be43-a668-421e-b8df-7246cdb40857\",\n \"first_name\": \"Alice\",\n \"last_name\": \"Hatter\",\n \"created_on\": \"2017-09-08T20:44:37\",\n \"updated_on\": \"2017-09-08T20:52:07\"\n },\n {\n \"id\": \"a593f5e2-e588-4e2a-ae57-c4dd8a3faed5\",\n \"first_name\": \"Mad\",\n \"last_name\": \"Hatter\",\n \"created_on\": \"2017-09-08T20:45:15\",\n \"updated_on\": \"2017-09-08T20:45:15\"\n },\n {\n \"id\": \"c761ef71-d4b0-4b14-aa45-549ffcb72234\",\n \"first_name\": \"Cheshire\",\n \"last_name\": \"Cat\",\n \"created_on\": \"2017-09-08T20:45:15\",\n \"updated_on\": \"2017-09-08T20:45:15\"\n }\n ]\n}\n```\n\nLet's change sorting order:\n```bash\ncurl -X GET \"http://localhost:5000/api/v1/clients?sort_by=updated_on,created_on,-id\"\n{\n \"offset\": 0,\n \"limit\": null,\n \"total_count\": 3,\n \"items_count\": 3,\n \"items_list\": [\n {\n \"id\": \"c761ef71-d4b0-4b14-aa45-549ffcb72234\",\n \"first_name\": \"Cheshire\",\n \"last_name\": \"Cat\",\n \"created_on\": \"2017-09-08T20:45:15\",\n \"updated_on\": \"2017-09-08T20:45:15\"\n },\n {\n \"id\": \"a593f5e2-e588-4e2a-ae57-c4dd8a3faed5\",\n \"first_name\": \"Mad\",\n \"last_name\": \"Hatter\",\n \"created_on\": \"2017-09-08T20:45:15\",\n \"updated_on\": \"2017-09-08T20:45:15\"\n },\n {\n \"id\": \"0372be43-a668-421e-b8df-7246cdb40857\",\n \"first_name\": \"Alice\",\n \"last_name\": \"Hatter\",\n \"created_on\": \"2017-09-08T20:44:37\",\n \"updated_on\": \"2017-09-08T20:52:07\"\n }\n ]\n}\n```\n\nLet's filter clients:\n```bash\ncurl -X GET \"http://localhost:5000/api/v1/clients?last_name=Hatter\"\n{\n \"offset\": 0,\n \"limit\": null,\n \"total_count\": 2,\n \"items_count\": 2,\n \"items_list\": [\n {\n \"id\": \"0372be43-a668-421e-b8df-7246cdb40857\",\n \"first_name\": \"Alice\",\n \"last_name\": \"Hatter\",\n \"created_on\": \"2017-09-08T20:44:37\",\n \"updated_on\": \"2017-09-08T20:52:07\"\n },\n {\n \"id\": \"a593f5e2-e588-4e2a-ae57-c4dd8a3faed5\",\n \"first_name\": \"Mad\",\n \"last_name\": \"Hatter\",\n \"created_on\": \"2017-09-08T20:45:15\",\n \"updated_on\": \"2017-09-08T20:45:15\"\n }\n ]\n}\n\nLet's filter clients by date range:\n```bash\ncurl -X GET \"http://localhost:5000/api/v1/clients?created_on__min=2017-09-08T20:00:00&created_on__max=2017-09-08T20:45:00\"\n{\n \"offset\": 0,\n \"limit\": null,\n \"total_count\": 1,\n \"items_count\": 1,\n \"items_list\": [\n {\n \"id\": \"0372be43-a668-421e-b8df-7246cdb40857\",\n \"first_name\": \"Alice\",\n \"last_name\": \"Hatter\",\n \"created_on\": \"2017-09-08T20:44:37\",\n \"updated_on\": \"2017-09-08T20:52:07\"\n }\n ]\n}\n```\n\nLet's filter clients by list of id:\n```bash\ncurl -X GET \"http://localhost:5000/api/v1/clients?id__in=0372be43-a668-421e-b8df-7246cdb40857,c761ef71-d4b0-4b14-aa45-549ffcb72234\"\n{\n \"offset\": 0,\n \"limit\": null,\n \"total_count\": 2,\n \"items_count\": 2,\n \"items_list\": [\n {\n \"id\": \"0372be43-a668-421e-b8df-7246cdb40857\",\n \"first_name\": \"Alice\",\n \"last_name\": \"Hatter\",\n \"created_on\": \"2017-09-08T20:44:37\",\n \"updated_on\": \"2017-09-08T20:52:07\"\n },\n {\n \"id\": \"c761ef71-d4b0-4b14-aa45-549ffcb72234\",\n \"first_name\": \"Cheshire\",\n \"last_name\": \"Cat\",\n \"created_on\": \"2017-09-08T20:45:15\",\n \"updated_on\": \"2017-09-08T20:45:15\"\n }\n ]\n}\n```\n\n\n", "description_content_type": null, "docs_url": null, "download_url": "https://github.com/left-join/flask-restive.git", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/left-join/flask-restive", "keywords": "flask,rest,api,flask_restful,marshmallow", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "Flask-RESTive", "package_url": "https://pypi.org/project/Flask-RESTive/", "platform": "a", "project_url": "https://pypi.org/project/Flask-RESTive/", "project_urls": { "Download": "https://github.com/left-join/flask-restive.git", "Homepage": "https://github.com/left-join/flask-restive" }, "release_url": "https://pypi.org/project/Flask-RESTive/0.0.3/", "requires_dist": [ "flask (>=0.12.2)", "flask-restful (>=0.3.6)", "marshmallow (>=3.0.0b3)", "six (>=1.10.0)" ], "requires_python": "", "summary": "Flask RESTive is a REST API Flask extension based on Flask-RESTful & Marshmallow.", "version": "0.0.3" }, "last_serial": 3179308, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "c31732655274a484c808993257afcb62", "sha256": "1864f2f5a8bfe7010baf5fd6a212d3e860a0d355b72c568359bbd6bfb7e5b21b" }, "downloads": -1, "filename": "Flask_RESTive-0.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "c31732655274a484c808993257afcb62", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 25383, "upload_time": "2017-09-10T18:12:19", "url": "https://files.pythonhosted.org/packages/f2/fa/65a5c3499b3dc311a0aee784e2c40c4bdad346b25d4b080cf32e6dd83756/Flask_RESTive-0.0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "0323c42e3e27682dc0cc03ba175628a3", "sha256": "9a2080551c4dc6ae062df9668ae2b521e7fb83437f1e128f2eca4cc434063f8b" }, "downloads": -1, "filename": "Flask-RESTive-0.0.1.tar.gz", "has_sig": false, "md5_digest": "0323c42e3e27682dc0cc03ba175628a3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20344, "upload_time": "2017-09-10T18:12:21", "url": "https://files.pythonhosted.org/packages/0a/15/cc239033a6f68d358202e0facebe3cc777f6d70ac7900c0843198832cd34/Flask-RESTive-0.0.1.tar.gz" } ], "0.0.2": [ { "comment_text": "", "digests": { "md5": "b590017978eabf0146a1ac065311a09c", "sha256": "dbc5593356d412297a5163f0f1be549ebd3139485503da5fb264b542389abbb7" }, "downloads": -1, "filename": "Flask_RESTive-0.0.2-py2-none-any.whl", "has_sig": false, "md5_digest": "b590017978eabf0146a1ac065311a09c", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 25756, "upload_time": "2017-09-13T15:22:56", "url": "https://files.pythonhosted.org/packages/67/82/f61dbaa5e2f63adb66ed6b3cc46f3b99deb9bcc11c6784c23a4573da77eb/Flask_RESTive-0.0.2-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "34105729f0f9cf7a6866346ed6a0bf46", "sha256": "c3be00645da95f5cdff12fc5acd3051ff14f11ecafbb2f69681308f402b29666" }, "downloads": -1, "filename": "Flask_RESTive-0.0.2-py3-none-any.whl", "has_sig": false, "md5_digest": "34105729f0f9cf7a6866346ed6a0bf46", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 25755, "upload_time": "2017-09-13T15:23:04", "url": "https://files.pythonhosted.org/packages/93/57/c883657d54b7ddf49fa543165a71a16b22263a77fadcabeb962e579ada8c/Flask_RESTive-0.0.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "232d317f7bdef5d3f6d125aa3e71de03", "sha256": "37ac7edecd6e01438d120f78feb664bb2234f67fd9eb04d542990192d90d8ef8" }, "downloads": -1, "filename": "Flask-RESTive-0.0.2.tar.gz", "has_sig": false, "md5_digest": "232d317f7bdef5d3f6d125aa3e71de03", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19659, "upload_time": "2017-09-13T15:22:57", "url": "https://files.pythonhosted.org/packages/e3/d7/d7112e639ee3256be33bb6a6741b0da096019f8a426ea50912560c3b982c/Flask-RESTive-0.0.2.tar.gz" } ], "0.0.3": [ { "comment_text": "", "digests": { "md5": "683496c39670c90764d5779f80626f64", "sha256": "e02f9c809dc162b4552dc9efd6db90063da7ce9a743734085883e8180435cd41" }, "downloads": -1, "filename": "Flask_RESTive-0.0.3-py2-none-any.whl", "has_sig": false, "md5_digest": "683496c39670c90764d5779f80626f64", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 25994, "upload_time": "2017-09-16T22:11:43", "url": "https://files.pythonhosted.org/packages/cd/bf/83a913089b6ac590e317dba0f79a27bf6611c25f4eea6c903cc3d75d2337/Flask_RESTive-0.0.3-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c85424e268daf0249119f91c377ae62a", "sha256": "30ab75d1f4f3eb749faabaeb9b7b2dc47d85129d63ab55105da5acac0920dbb8" }, "downloads": -1, "filename": "Flask_RESTive-0.0.3-py3-none-any.whl", "has_sig": false, "md5_digest": "c85424e268daf0249119f91c377ae62a", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 25989, "upload_time": "2017-09-16T22:12:35", "url": "https://files.pythonhosted.org/packages/2b/2c/06de5810071fd0f775f3201900386edf507a8d831d72508740e1b59220ac/Flask_RESTive-0.0.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "8d3841aebf8849a2ea7d66d51286cd02", "sha256": "a84e7349fef68bfc96043125ed21f759f0003447dd453b4d57647b217d65e2cf" }, "downloads": -1, "filename": "Flask-RESTive-0.0.3.tar.gz", "has_sig": false, "md5_digest": "8d3841aebf8849a2ea7d66d51286cd02", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19677, "upload_time": "2017-09-16T22:11:57", "url": "https://files.pythonhosted.org/packages/56/7d/2c109cc9e3134ec14a4c09bc7dc69d8de913cd0d2ab904b3e5a9bfbe61f4/Flask-RESTive-0.0.3.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "683496c39670c90764d5779f80626f64", "sha256": "e02f9c809dc162b4552dc9efd6db90063da7ce9a743734085883e8180435cd41" }, "downloads": -1, "filename": "Flask_RESTive-0.0.3-py2-none-any.whl", "has_sig": false, "md5_digest": "683496c39670c90764d5779f80626f64", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 25994, "upload_time": "2017-09-16T22:11:43", "url": "https://files.pythonhosted.org/packages/cd/bf/83a913089b6ac590e317dba0f79a27bf6611c25f4eea6c903cc3d75d2337/Flask_RESTive-0.0.3-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c85424e268daf0249119f91c377ae62a", "sha256": "30ab75d1f4f3eb749faabaeb9b7b2dc47d85129d63ab55105da5acac0920dbb8" }, "downloads": -1, "filename": "Flask_RESTive-0.0.3-py3-none-any.whl", "has_sig": false, "md5_digest": "c85424e268daf0249119f91c377ae62a", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 25989, "upload_time": "2017-09-16T22:12:35", "url": "https://files.pythonhosted.org/packages/2b/2c/06de5810071fd0f775f3201900386edf507a8d831d72508740e1b59220ac/Flask_RESTive-0.0.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "8d3841aebf8849a2ea7d66d51286cd02", "sha256": "a84e7349fef68bfc96043125ed21f759f0003447dd453b4d57647b217d65e2cf" }, "downloads": -1, "filename": "Flask-RESTive-0.0.3.tar.gz", "has_sig": false, "md5_digest": "8d3841aebf8849a2ea7d66d51286cd02", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19677, "upload_time": "2017-09-16T22:11:57", "url": "https://files.pythonhosted.org/packages/56/7d/2c109cc9e3134ec14a4c09bc7dc69d8de913cd0d2ab904b3e5a9bfbe61f4/Flask-RESTive-0.0.3.tar.gz" } ] }