{ "info": { "author": "Anton Gilgur", "author_email": "", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "Framework :: Django", "Framework :: Django :: 1.10", "Framework :: Django :: 1.11", "Framework :: Django :: 1.4", "Framework :: Django :: 1.5", "Framework :: Django :: 1.6", "Framework :: Django :: 1.7", "Framework :: Django :: 1.8", "Framework :: Django :: 1.9", "Framework :: Django :: 2.0", "Framework :: Django :: 2.1", "Framework :: Django :: 2.2", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Topic :: Software Development :: Libraries" ], "description": "# django-api-decorators\n\n\n[![PyPI version](https://img.shields.io/pypi/v/django-api-decorators.svg)](https://pypi.org/project/django-api-decorators/)\n[![releases](https://img.shields.io/github/tag-pre/agilgur5/django-api-decorators.svg)](https://github.com/agilgur5/django-api-decorators/releases)\n[![commits](https://img.shields.io/github/commits-since/agilgur5/django-api-decorators/latest.svg)](https://github.com/agilgur5/django-api-decorators/commits/master)\n
\n[![dm](https://img.shields.io/pypi/dm/django-api-decorators.svg)](https://pypi.org/project/django-api-decorators/)\n[![dw](https://img.shields.io/pypi/dw/django-api-decorators.svg)](https://pypi.org/project/django-api-decorators/)\n\nTiny decorator functions to make it easier to build an API using Django in ~100 LoC (shorter than this README!)\n\n## Table of Contents\n\nI. [Installation](#installation)
\nII. [Usage](#usage)
\nIII. [How it Works](#how-it-works)
\nIV. [Related Libraries](#related-libraries)
\nV. [Backstory](#backstory)\n\n## Installation\n\n```shell\npip install django-api-decorators\n```\n\nIt is expected that you already have Django installed\n\n### Compatibility\n\n_This was originally used in an older Django 1.5 codebase with Python 2.7._\n\nShould work with Django 1.x-2.x and with Python 2.7-3.x\n\n- Likely works with Django 0.95-0.99 as well, didn't check any earlier versions' release notes\n- `2to3` shows that there is nothing to change, so should be compatible with Python 3.x\n- Have not confirmed if this works with earlier versions of Python.\n\nPlease submit a PR or file an issue if you have a compatibility problem or have confirmed compatibility on versions.\n\n
\n\n## Usage\n\n`@method_exclusive`, per docstring: `Checks if request.method is equal to method, if not, returns a 405 not allowed response`. Example:\n\n```python\nfrom django_api_decorators import method_exlusive\n\n@method_exclusive('GET')\ndef get_latest_public_posts(request):\n ...\n\n```\n\n
\n\n`@require_auth`, per docstring: `Checks if the request was made by an authenticated user, and if not, returns a 401 unauthorized response`. Example:\n\n```python\nfrom django_api_decorators import method_exclusive, require_auth\n\n@method_exclusive('GET')\n@require_auth\ndef get_favorites(request):\n favs = request.user.favorites.all()\n ...\n\n```\n\nOne can add more authorization checks on the User, such as for specific user types,\nby building on top of the `@require_auth` decorator. For instance:\n\n```python\nfrom functools import wraps\n\nfrom django.http import HttpResponse\nfrom django_api_decorators import require_auth\n\ndef tenant_exclusive(func):\n \"\"\"\n Checks if the authenticated user is a tenant, and if not, returns a\n 401 unauthorized response\n \"\"\"\n @wraps(func)\n @require_auth\n def func_wrapper(request, *args, **kwargs):\n if not request.user.is_tenant():\n return HttpResponse(status=401)\n return func(request, *args, **kwargs)\n return func_wrapper\n```\n\n
\n\n`@clean_form`, per docstring: `Cleans the data in the POST or GET params using the form_class specified. Responds with a 400 bad request if the form is invalid with the errors specified in the form as JSON. Adds the cleaned data as a kwarg (cd) to the decorated function`. Example:\n\n```python\nfrom django.shortcuts import get_object_or_404\nfrom django_api_decorators import method_exclusive, clean_form, require_auth\n\nfrom posts.models import Post\nfrom posts.forms import AddFavForm\n\n@method_exclusive('POST')\n@clean_form(AddFavForm)\n@require_auth\ndef add_fav(request, cd):\n post = get_object_or_404(Post, pk=cd['post_id'])\n request.user.favorites.add(post)\n ...\n\n```\n\n
\n\n`@clean_forms`, per docstring: `Cleans the data in the POST or GET params using the form_class specified. Responds with a 400 bad request if any of the forms are invalid with the errors specified in the form as JSON. Adds the cleaned data as a kwarg (cd_list) to the decorated function`. Example:\n\n```python\nfrom django_api_decorators import method_exclusive, clean_forms, require_auth\n\nfrom posts.models import Post\nfrom posts.forms import CreatePostForm\n\n@method_exclusive('POST')\n@clean_forms(CreatePostForm, 'posts')\n@require_auth\ndef bulk_create_posts(request, cd_list):\n post_list = []\n for data in cd_list:\n post_list.append(Post(\n user=request.user,\n cotent=data['content']\n ))\n Post.objects.bulk_create(post_list)\n\n ...\n\n```\n\n
\n\n## How it works\n\nAll of the decorators currently just perform a check against the `request` object, have an early return if the request is invalid, and otherwise let the next function execute. Some of them add a keyword argument when calling the next function so that the interpreted data can be used within it (like with the cleaned dictionaries of forms, which are added as a `kwarg` of the keyword `cd`).\n\nI'd encourage you to read the source code, since it's shorter than this README :)\n\n## Related Libraries\n\n- [django-serializable-model](https://github.com/agilgur5/django-serializable-model)\n - `Django classes to make your models, managers, and querysets serializable, with built-in support for related objects in ~100 LoC`\n\n
\n\n## Backstory\n\nThis library was built while I was working on [Yorango](https://github.com/Yorango)'s ad-hoc API and transitioning from an MPA to an SPA. Instead of repeating lots of authentication, authorization, and validation code for every request, I wanted to DRY it up more using decorators or middleware. Decorators would allow us to have early returns with proper HTTP Status Codes for invalid requests. Request code became easier to reason about as a result, guaranteeing it would only execute after authz/authn/etc, and much less prone to accidental bugs, e.g. security issues due to forgetting an authorization check. `@method_exclusive`, `@require_auth`, and a few more project-specific decorators were born out of some of those needs.\n\nValidation was a bit more difficult, as we had many existing [Django `Form`s](https://docs.djangoproject.com/en/2.0/ref/forms/api/#django.forms.Form) in the MPA, wanted to re-use the classes and validation code we already had in our API instead of re-writing, and wanted to keep things in the same idiomatic style. [Django REST Framework has the concept of \"Validators\"](http://www.django-rest-framework.org/api-guide/validators/), but it is explicitly different from Django's standard `Form` interface and requires you to buy-in to other parts of DRF to use, like Serializers. `@clean_form` was born to address those needs. Later `@clean_forms` was made to address the case of multiple of the same form in one API request (e.g. for bulk creation), somewhat similar to how a [Django `FormSet`](https://docs.djangoproject.com/en/2.0/topics/forms/formsets/) might work, but much simpler and requiring a lot less coupling of front and back end code.\n\nThese were all used in production with great results, some API methods having just 1 decorator and others having 3 or more decorators, such as:\n\n```python\n@method_exclusive('POST')\n@clean_form(CreateBillsForm)\n@clean_forms(BillForm, 'bills')\n@landlord_saas_exclusive\n@authorize_action(Listing, 'listing_id')\ndef create_bills(request, cd, cd_list, listing):\n ...\n\n # bulk_create the new list of Bills\n bill_list = []\n for data in cd_list:\n bill_list.append(Bill(\n listing=listing,\n price=data['price'],\n due_date=data['due_date'],\n ))\n Bill.objects.bulk_create(bill_list)\n\n ...\n```\n\nHad been meaning to extract and open source this as well as other various useful utility libraries I had made at Yorango and finally got the chance!\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/agilgur5/django-api-decorators", "keywords": "django api rest ad-hoc decorator json dict form formset validation", "license": "Apache-2.0", "maintainer": "", "maintainer_email": "", "name": "django-api-decorators", "package_url": "https://pypi.org/project/django-api-decorators/", "platform": "", "project_url": "https://pypi.org/project/django-api-decorators/", "project_urls": { "Homepage": "https://github.com/agilgur5/django-api-decorators", "Source": "https://github.com/agilgur5/django-api-decorators/", "Tracker": "https://github.com/agilgur5/django-api-decorators/issues" }, "release_url": "https://pypi.org/project/django-api-decorators/0.0.3/", "requires_dist": null, "requires_python": ">=2.7, <4", "summary": "Tiny decorator functions to make it easier to build an API using Django in ~100 LoC", "version": "0.0.3" }, "last_serial": 5712666, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "827e94e1ac3bfed50785fbae0bdd4abd", "sha256": "684c1dc1da71395a542941d74601b8fcfeb2c1f1139c40b003f51a559bba9537" }, "downloads": -1, "filename": "django_api_decorators-0.0.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "827e94e1ac3bfed50785fbae0bdd4abd", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": ">=2.7, <4", "size": 5220, "upload_time": "2018-07-31T01:44:54", "url": "https://files.pythonhosted.org/packages/72/ed/d1783890636dab07d60abef1c722feb78a4505d81a9fcd4c3cf07d7f0009/django_api_decorators-0.0.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "77360dd99e57022bfcc91b10bc578ca0", "sha256": "5eaf95843ceccd691bba115caacd060375c479b1ce0e9a40857f33b45f629713" }, "downloads": -1, "filename": "django-api-decorators-0.0.1.tar.gz", "has_sig": false, "md5_digest": "77360dd99e57022bfcc91b10bc578ca0", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.7, <4", "size": 5683, "upload_time": "2018-07-31T01:44:55", "url": "https://files.pythonhosted.org/packages/a6/47/11203bd5deb0932933ba9db193aff64ca7be7bc844d5cce35a8fc436ec91/django-api-decorators-0.0.1.tar.gz" } ], "0.0.2": [ { "comment_text": "", "digests": { "md5": "387f078da8cb7f2e8c71fa8ef91fc16e", "sha256": "4311abccad74a2cab470168fc3f940277480146b82488d5cfd42a826309ed0de" }, "downloads": -1, "filename": "django_api_decorators-0.0.2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "387f078da8cb7f2e8c71fa8ef91fc16e", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": ">=2.7, <4", "size": 5229, "upload_time": "2018-07-31T02:18:59", "url": "https://files.pythonhosted.org/packages/57/dd/ad602cefc4f588994fc48559ab8f173711318d50c18b2f7b403f37256bd2/django_api_decorators-0.0.2-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "33035e031cd7c47cf8a0fea363a9772a", "sha256": "4e179ad7c5b60f8304be344344142ada281c66153e153de8440e74aaaf12727d" }, "downloads": -1, "filename": "django-api-decorators-0.0.2.tar.gz", "has_sig": false, "md5_digest": "33035e031cd7c47cf8a0fea363a9772a", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.7, <4", "size": 5681, "upload_time": "2018-07-31T02:19:00", "url": "https://files.pythonhosted.org/packages/76/4d/cbd2d3f67ab9c7340d4ed30cd4913203d65e929f51bd996bad4d198c6b39/django-api-decorators-0.0.2.tar.gz" } ], "0.0.3": [ { "comment_text": "", "digests": { "md5": "24d05ef9a9c3c091e36fd8343af11aba", "sha256": "d2cfe9fad26b07eb4ea4528940e799334286942d17d2299b7611ac9392587708" }, "downloads": -1, "filename": "django_api_decorators-0.0.3-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "24d05ef9a9c3c091e36fd8343af11aba", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": ">=2.7, <4", "size": 5936, "upload_time": "2019-08-22T01:50:21", "url": "https://files.pythonhosted.org/packages/36/e1/2d3a8b81e00fb0fe3baaffba907bff9fb6f783b0e5c3874e9771e3fe30f9/django_api_decorators-0.0.3-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "bff9c1093bc7f874a9810c28fd958707", "sha256": "21c4414485d7a35ffe483640d9c104363efe22c397fd724348efeed10646c43d" }, "downloads": -1, "filename": "django-api-decorators-0.0.3.tar.gz", "has_sig": false, "md5_digest": "bff9c1093bc7f874a9810c28fd958707", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.7, <4", "size": 6409, "upload_time": "2019-08-22T01:50:23", "url": "https://files.pythonhosted.org/packages/ac/0a/fc7fd556910234af340ac1877e53d8efe8fb017ac99aaed14fd5a9ad57b6/django-api-decorators-0.0.3.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "24d05ef9a9c3c091e36fd8343af11aba", "sha256": "d2cfe9fad26b07eb4ea4528940e799334286942d17d2299b7611ac9392587708" }, "downloads": -1, "filename": "django_api_decorators-0.0.3-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "24d05ef9a9c3c091e36fd8343af11aba", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": ">=2.7, <4", "size": 5936, "upload_time": "2019-08-22T01:50:21", "url": "https://files.pythonhosted.org/packages/36/e1/2d3a8b81e00fb0fe3baaffba907bff9fb6f783b0e5c3874e9771e3fe30f9/django_api_decorators-0.0.3-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "bff9c1093bc7f874a9810c28fd958707", "sha256": "21c4414485d7a35ffe483640d9c104363efe22c397fd724348efeed10646c43d" }, "downloads": -1, "filename": "django-api-decorators-0.0.3.tar.gz", "has_sig": false, "md5_digest": "bff9c1093bc7f874a9810c28fd958707", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.7, <4", "size": 6409, "upload_time": "2019-08-22T01:50:23", "url": "https://files.pythonhosted.org/packages/ac/0a/fc7fd556910234af340ac1877e53d8efe8fb017ac99aaed14fd5a9ad57b6/django-api-decorators-0.0.3.tar.gz" } ] }