{ "info": { "author": "Justin Walters", "author_email": "walters.justin01@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "Environment :: Web Environment", "Framework :: Django :: 1.10", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Database :: Front-Ends", "Topic :: Internet :: WWW/HTTP :: WSGI :: Application" ], "description": "=============\nDjango RestUp\n=============\n**DO NOT USE IN PRODUCTION!!!**\nThis package is in very early alpha and has no tests! It is currently more of\na proof of concept.\n\nPhilosophy\n----------\nDjango RestUp takes time-tested ideas from other Django REST API packages such\nas Django REST Framework, Django Tastypie, and Restless.\n\nIt expands upon these ideas by applying a bit more flexibility/extensibility and\nexplicitness. There is a trade-off. RestUp requires a bit more configuration than\nother packages/libraries.\n\nThe main difference between RestUp and other packages is how RestUp determines API\ndata structure. RestUp requires the developer to explicitly declare the structure of\ntheir data on a per-resource basis.\n\nThis explicitness comes with some major benefits:\n\n- An extra layer of security and sanity checking for your data.\n- Makes it easy to see the structure of your data and make changes to that\n structure without side effects.\n- Expose only the data you want to expose, and in the manner that you want\n to expose it.\n- Allows for deep authentication and authorization on a per-resource,\n per-object, and even per-field basis.\n\n===============\nGetting Started\n===============\nSchema Definition\n-----------------\nAt the core of a RestUp ``ModelResource`` is it's schema definition. You\ndefine a schema as a native python dictionary object. This schema is used to\npopulate the JSON response for ``GET`` requests. It is also used to help\ndeserialize ``POST`` ed data into native python data types so it can be used to\ncreate a new object or modify an existing one.\n\n**Example:**\nAssume we have a Django model with the following structure::\n\n # In ~/yourapp/models.py\n from django.db import models\n\n\n class Book(models.Model):\n\n title = models.CharField(\n max_lenfth=200,\n blank=False\n )\n\n isbn = models.CharField(\n max_length=100,\n blank=False\n )\n\n favorites = models.PositiveIntegerField(\n blank=True,\n default=0\n )\n\n secret_field = models.CharField(\n max_length=20,\n blank=False\n )\n\nLet's define a RestUp ``ModelResource`` class to expose this resource::\n\n # In ~/yourapp/api.py\n from restup import ModelResource\n from .models import Book\n\n\n class BookResource(ModelResource):\n\n model = Book\n\n schema = {\n 'title': {\n 'attribute': 'title'\n },\n 'isbn': {\n 'attribute': 'isbn'\n },\n 'favorites': {\n 'attribute': 'favorites'\n },\n 'secret_field': {\n 'attribute': 'secret_field'\n }\n }\n\nYou'll also need to tell Django where to find the urls for this resource::\n\n # In ~/yourproject/urls.py\n\n # ~~? (other imports)\n from django.conf.urls import include\n from yourapp.api import BookResource\n\n urlpatterns = [\n # ...?\n url(\n r'^api/books/', include(BookResource.urls())\n )\n ]\n\nThat's all you need to define in order to make any ``GET, POST, PUT, DELETE``\nrequests to the endpoint.\n\nPermissions\n-----------\nHowever, right now, we're letting just anyone mess with our data! There's\nno security at all! We only want logged in users to be able to manipulate\nthe data. We'll allow anyone to ``GET`` it though::\n\n # In ~/yourapp/api.py\n from restup import ModelResource\n from .models import Book\n\n\n class BookResource(ModelResource):\n\n model = Book\n\n schema = {\n 'title': {\n 'attribute': 'title'\n },\n 'isbn': {\n 'attribute': 'isbn'\n },\n 'favorites': {\n 'attribute': 'favorites'\n },\n 'secret_field': {\n 'attribute': 'secret_field'\n }\n }\n\n def is_authenticated(self, request): # Add this method override\n if request.method in self.SAFE_METHODS:\n return True\n return request.user.is_authenticated()\n\nYou can put anything you want in the ``is_authenticated`` method as long as it\nreturns true for authenticated requests and false for unauthorized requests. You'll\nnotice that you have access to the request object. This is a normal Django request\nobject. You can do anything with it that you could do in a normal Django view class.\nThe ``is_authenticated`` method is the second permission hook to be called in any\nrequest. It is called right after the allowed methods check and right before the\nrequest is routed to the correct action handler. Speaking of the Allowed methods\ncheck, we don't want anyone to be able to delete our models. Let's stop them\nfrom doing that::\n\n # In ~/yourapp/api.py\n from restup import ModelResource\n from .models import Book\n\n\n class BookResource(ModelResource):\n\n model = Book\n\n schema = {\n 'title': {\n 'attribute': 'title'\n },\n 'isbn': {\n 'attribute': 'isbn'\n },\n 'favorites': {\n 'attribute': 'favorites'\n },\n 'secret_field': {\n 'attribute': 'secret_field'\n }\n }\n\n def is_authenticated(self, request):\n if request.method in self.SAFE_METHODS:\n return True\n return request.user.is_authenticated()\n\n def can_delete(self, obj, request): # Override this method\n return False\n\nThere we go! Now all ``DELETE`` requests to any of the ``BookResource`` endpoints\nwill return a ``403 Forbidden`` HTTP response. What about the ``secret_field``\nfield? Surely we don't want everyone to see that? But, we need to populate it with\ndata from the client. This is where RestUp becomes something special::\n\n # In ~/yourapp/api.py\n from restup import ModelResource\n from .models import Book\n\n\n class BookResource(ModelResource):\n\n model = Book\n\n schema = {\n 'title': {\n 'attribute': 'title'\n },\n 'isbn': {\n 'attribute': 'isbn'\n },\n 'favorites': {\n 'attribute': 'favorites'\n },\n 'secret_field': {\n 'attribute': 'secret_field',\n 'readable': False # Add this line\n }\n }\n\n def is_authenticated(self, request):\n if request.method in self.SAFE_METHODS:\n return True\n return request.user.is_authenticated()\n\n def can_delete(self, obj, request):\n return False\n\nAll we need to do is add a ``'readable'`` key to our field declaration inside\nour schema and set it's value to ``False``. This will ensure that this data is\nnot sent out to any requesting client. However, we can still apply ``POST`` ed\ndata to this field.\n\nObject level permissions\n------------------------\nWe only want staff users to be able to create and update any ``Book`` objects.\nLet's make sure no one else can::\n\n # In ~/yourapp/api.py\n from restup import ModelResource\n from .models import Book\n\n\n class BookResource(ModelResource):\n\n model = Book\n\n schema = {\n 'title': {\n 'attribute': 'title'\n },\n 'isbn': {\n 'attribute': 'isbn'\n },\n 'favorites': {\n 'attribute': 'favorites'\n },\n 'secret_field': {\n 'attribute': 'secret_field',\n 'readable': False\n }\n }\n\n def is_authenticated(self, request):\n if request.method in self.SAFE_METHODS:\n return True\n return request.user.is_authenticated()\n\n def can_create(self, request): # Override this method\n return request.user.is_staff\n\n def can_delete(self, obj, request):\n return False\n\n def can_update(self, obj, request): # Override this method\n return request.user.is_staff\n\nThere we go! Now only staff members can create and update ``Book`` resources!\n\nWe now have a fairly robust RESTful resource. Our resource allows us to\ncreate, update, list, and get ``Book`` objects. We also make sure non staff users\ncan't do anything but ``GET`` the resource from either it's list or detail endpoints.\n\nFiltering\n---------\nWe want users to be able to filter ``Book`` objects. We'll allow them to\nfilter the results based on the favorites field::\n\n # In ~/yourapp/api.py\n from restup import ModelResource\n from .models import Book\n\n\n class BookResource(ModelResource):\n\n model = Book\n\n schema = {\n 'title': {\n 'attribute': 'title'\n },\n 'isbn': {\n 'attribute': 'isbn'\n },\n 'favorites': {\n 'attribute': 'favorites',\n 'filters': ( # Add this key\n 'gt', 'lt',\n )\n },\n 'secret_field': {\n 'attribute': 'secret_field',\n 'readable': False\n }\n }\n\n def is_authenticated(self, request):\n if request.method in self.SAFE_METHODS:\n return True\n return request.user.is_authenticated()\n\n def can_create(self, request):\n return request.user.is_staff\n\n def can_delete(self, obj, request):\n return False\n\n def can_update(self, obj, request):\n return request.user.is_staff\n\nGreat! Now, if our users want to get a list of books with more than 10 favorites\nthey only need to send a request to ``http://mysite.com/api/books/?favorites__gt=10``.\nYou can use any of the standard Django query filters defined\n`in the Django docs `_\n\nValidation\n----------\nWe want to be sure that the client is only providing a positive integer to our\nfavorites field. We will create 2 validator functions to make sure the value\nis correct::\n\n # In ~/yourapp/api.py\n from restup import ModelResource\n from .models import Book\n\n # We will add two methods for illustrative purposes, but this check could\n # easily be done with a single function.\n\n def is_integer(value): # Add this method\n return type(value) == int\n\n def is_positive(value): # Add this method\n return value > 0\n\n\n class BookResource(ModelResource):\n\n model = Book\n\n schema = {\n 'title': {\n 'attribute': 'title'\n },\n 'isbn': {\n 'attribute': 'isbn'\n },\n 'favorites': {\n 'attribute': 'favorites',\n 'filters': (\n 'gt', 'lt',\n ),\n 'validators': ( # Add this key\n is_integer,\n is_positive\n )\n },\n 'secret_field': {\n 'attribute': 'secret_field',\n 'readable': False\n }\n }\n\n def is_authenticated(self, request):\n if request.method in self.SAFE_METHODS:\n return True\n return request.user.is_authenticated()\n\n def can_create(self, request):\n return request.user.is_staff\n\n def can_delete(self, obj, request):\n return False\n\n def can_update(self, obj, request):\n return request.user.is_staff\n\nValidator functions should take a single argument. This argument should be\nthe ``value`` of the key in the received data. The function should return\n``True`` if the value is valid and ``False`` otherwise. A check like the\none in our example isn't srictly necessary as Django's model backend\nwould throw an exception if we tried to save anything that wasn't a positive\ninteger. However, that would return a ``500 Server error`` response. That's not\nvery helpful for the client. It would be much better to return a ``400 Bad Request``\nresponse to let the client know they entered something incorrectly.\n\n==========\nConclusion\n==========\nWell, that's a basic rundown of how it all works. If you want a more in-depth\nunderstanding, please take a look at the source code. The ``ModelResource``\nclass is a great place to start.\n\nDocumentation\n-------------\nAs the project develops, I plan on adding\nmore complete documentation including:\n\n- An in-depth tutorial\n- Full API reference\n- In-depth explanation of data flow\n\nUpcoming required development\n-----------------------------\n- Tests!\n- Robust handling for edge-cases.\n\nUpcoming Features\n-----------------\nA list of some features on the docket:\n\n- Support for custom per-field pre and post processing functions. These\n will take a value returned by the database or from client ``POST`` ed data\n and perform any necessary complex transformations.\n- Support for custom per-field authorization functions for extremely granular\n permissions control.\n- Self documenting schema endpoints similar to those of Django REST Framework.\n- Return resource URIs in JSON data.\n- Custom URL namespaces.\n\nFeatures I won't add\n--------------------\n- XML/YAML/etc support. I don't use these often and they aren't very easy to\n serialize. If someone else wants to add support, they are welcome to create\n a pull request.\n- Python 2 support. Sorry, but it's time to move on.\n- Django < 1.8 support. See above.\n\n============\nContributing\n============\nThis project is in very early development. It should only be used on non-production\nprojects until it reaches ``V1.0.0``.\n\nAny criticisms or ideas welcome. Just open up an issue.\n\nIf you want to contribute to the source code, it is preferred that you open an issue\nbefore submitting a pull request to discuss the changes or enhancements you want to\nmake. I will not discriminate against anyone for any reason.", "description_content_type": null, "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/FFX01/django-restup", "keywords": "django rest api", "license": "BSD", "maintainer": "", "maintainer_email": "", "name": "django-restup", "package_url": "https://pypi.org/project/django-restup/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/django-restup/", "project_urls": { "Homepage": "https://github.com/FFX01/django-restup" }, "release_url": "https://pypi.org/project/django-restup/0.1.1/", "requires_dist": null, "requires_python": "", "summary": "A Library for creating REST APIs for Django applications", "version": "0.1.1" }, "last_serial": 2417936, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "5fa0d20b5d60d896a41d7160e8b6d072", "sha256": "a53f0c7753f57b5a74a4d7da31de318daf2405259ec33ad34e886c8d037eb863" }, "downloads": -1, "filename": "django_restup-0.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "5fa0d20b5d60d896a41d7160e8b6d072", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 9880, "upload_time": "2016-10-23T00:45:30", "url": "https://files.pythonhosted.org/packages/43/ac/99e8e94e735c9b15bf798fd370a6ab04eaa0e272c72b51dcae1a450dc869/django_restup-0.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ab43cbcb67ddfbf92d22d110834fa8e8", "sha256": "ad629117d8c8f9df6f04d3368c70506eec6e5a5ab5421f9df0245d27bd036c08" }, "downloads": -1, "filename": "django-restup-0.1.0.tar.gz", "has_sig": false, "md5_digest": "ab43cbcb67ddfbf92d22d110834fa8e8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8296, "upload_time": "2016-10-23T00:45:33", "url": "https://files.pythonhosted.org/packages/40/cb/cf5b4c413b1774efbb3751a63c733d95db175bb442cd09cbc565231e4e83/django-restup-0.1.0.tar.gz" } ], "0.1.1": [ { "comment_text": "", "digests": { "md5": "c2d18d17c42bd04a4e26b0483b69eaec", "sha256": "680231479f93b8a97dcfefdc17bdc0d3e609bce6250fedfa3320628ecee8e26c" }, "downloads": -1, "filename": "django_restup-0.1.1-py3-none-any.whl", "has_sig": false, "md5_digest": "c2d18d17c42bd04a4e26b0483b69eaec", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 17505, "upload_time": "2016-10-23T04:34:55", "url": "https://files.pythonhosted.org/packages/be/02/220652743f2937bc60e702d140ff8b2128b9d185d5464389c8b0c177731b/django_restup-0.1.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "d87cc3ca028066ad2ce16d4f640bbef0", "sha256": "3fffeaf97de59961dc9e9298f641a311314499196ce0550c35c172ddae2e910f" }, "downloads": -1, "filename": "django-restup-0.1.1.tar.gz", "has_sig": false, "md5_digest": "d87cc3ca028066ad2ce16d4f640bbef0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14123, "upload_time": "2016-10-23T04:34:33", "url": "https://files.pythonhosted.org/packages/74/bc/281b0fa3762c30c09f74ef3ced0f953af20d6d79ef21068fd08a9664e40e/django-restup-0.1.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "c2d18d17c42bd04a4e26b0483b69eaec", "sha256": "680231479f93b8a97dcfefdc17bdc0d3e609bce6250fedfa3320628ecee8e26c" }, "downloads": -1, "filename": "django_restup-0.1.1-py3-none-any.whl", "has_sig": false, "md5_digest": "c2d18d17c42bd04a4e26b0483b69eaec", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 17505, "upload_time": "2016-10-23T04:34:55", "url": "https://files.pythonhosted.org/packages/be/02/220652743f2937bc60e702d140ff8b2128b9d185d5464389c8b0c177731b/django_restup-0.1.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "d87cc3ca028066ad2ce16d4f640bbef0", "sha256": "3fffeaf97de59961dc9e9298f641a311314499196ce0550c35c172ddae2e910f" }, "downloads": -1, "filename": "django-restup-0.1.1.tar.gz", "has_sig": false, "md5_digest": "d87cc3ca028066ad2ce16d4f640bbef0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14123, "upload_time": "2016-10-23T04:34:33", "url": "https://files.pythonhosted.org/packages/74/bc/281b0fa3762c30c09f74ef3ced0f953af20d6d79ef21068fd08a9664e40e/django-restup-0.1.1.tar.gz" } ] }