{ "info": { "author": "Craig Labenz, Jo\u00e3o Luiz Lorencetti (Fork author)", "author_email": "jll.linux@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Environment :: Web Environment", "Framework :: Django", "Framework :: Django :: 1.11", "Framework :: Django :: 2.0", "Framework :: Django :: 2.1", "Framework :: Django :: 2.2", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Topic :: Internet :: WWW/HTTP", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "[![Build Status](https://travis-ci.org/jllorencetti/drf-jsonmask.svg?branch=master)](https://travis-ci.org/jllorencetti/drf-jsonmask.svg)\n[![Coverage Status](https://coveralls.io/repos/github/jllorencetti/drf-jsonmask/badge.svg?branch=master)](https://coveralls.io/github/jllorencetti/drf-jsonmask?branch=master)\n[![PyPI Version](https://img.shields.io/pypi/v/drf-jsonmask.svg)](https://pypi.org/project/drf-jsonmask)\n\n---\n\n## Overview\n\nForked from https://github.com/zapier/django-rest-framework-jsonmask.\n\nImplements Google's Partial Response in Django RestFramework.\n\n## Requirements\n\n* Python (3.6, 3.7)\n* Django (1.11, 2.0, 2.1)\n\n## Installation\n\nInstall using `pip`...\n\n```bash\n$ pip install drf-jsonmask\n```\n\n## Example\n\nMost DRF addons that support `?fields=`-style data pruning do so purely at the serializaton layer. Many hydrate full ORM objects, including all of their verbose relationships, and then cut unwanted data immediately before JSON serialization. Any unwanted related data is still fetched from the database and hydrated into Django ORM objects, which severely undermines the usefulness of field pruning.\n\n`drf_jsonmask` aims to do one better by allowing developers to declaratively augment their queryset in direct relation to individual requests. Under this pattern, you only declare the base queryset and any universal relationships on your ViewSet.queryset, leaving all additional enhancements as runtime opt-ins.\n\nTo use `drf_jsonmask`, first include its ViewSet and Serializer mixins in your code where appropriate. The following examples are taken from the mini-project used in this library's own unit tests.\n\n```py\n# api/views.py\nfrom drf_jsonmask.views import OptimizedQuerySetMixin\n\nclass TicketViewSet(OptimizedQuerySetMixin, viewsets.ReadOnlyModelViewSet):\n\n # Normally, for optimal performance, you would apply the `select_related('author')`\n # call to the base queryset, but that is no longer desireable for data relationships\n # that your frontend may stop asking for.\n queryset = Ticket.objects.all()\n serializer_class = TicketSerializer\n\n # Data-predicate declaration is optional, but encouraged. This\n # is where the library really shines!\n @data_predicate('author')\n def load_author(self, queryset):\n return queryset.select_related('author')\n\n\n# api/serializers.py\nfrom drf_jsonmask.serializers import FieldsListSerializerMixin\n\nclass TicketSerializer(FieldsListSerializerMixin, serializers.ModelSerializer):\n # Aside from the mixin, everything else is exactly like normal\n\n author = UserSerializer()\n\n class Meta:\n models = my_module.models.Ticket\n fields = ('id', 'title', 'body', 'author',)\n```\n\nYou have now set up your API to skip unnecessary joins (and possibly prefetches), unless the requesting client requires that data. Let's consider a few hypothetical requests and the responses they will each receive. (For brevity, in each of these examples, I will pretend pagination is turned off.)\n\n```http\nGET /api/tickets/\n\n200 OK\n[\n {\n \"id\": 1,\n \"title\": \"This is a ticket\",\n \"body\": \"This is its text\",\n \"author\": {\n \"id\": 5,\n \"username\": \"HomerSimpson\",\n }\n }\n]\n```\n\nBecause no `?fields` querystring parameter was provided, author records were still loaded and serialized like normal.\n\n> Note: `drf_jsonmask` treats all requests that lack any field definition as if all possible data is requested, and thus executes all data predicates. In the above example, `author` data was loaded via `selected_related('author')`, and _not_ N+1 queries.\n\n---\n\n```http\nGET /api/tickets/?fields=id,title,body\n\n200 OK\n[\n {\n \"id\": 1,\n \"title\": \"This is a ticket\",\n \"body\": \"This is its text\"\n }\n]\n```\n\nIn this example, since `author` was not specified, it was not only not returned in the response payload - it was never queried for or serialized in the first place.\n\n---\n\n```http\nGET /api/tickets/?fields=id,title,body,author/username\n\n200 OK\n[\n {\n \"id\": 1,\n \"title\": \"This is a ticket\",\n \"body\": \"This is its text\",\n \"author\": {\n \"username\": \"HomerSimpson\",\n }\n }\n]\n```\n\nIn this example, `author` data was loaded via the `?fields` declaration, but no unwanted keys will appear in the response.\n\n\n#### Nested Relationships\n\nThis is all good and fun, but what if `author` has rarely used but expensive relationships, too? `drf_jsonmask` supports this, via the exact same mechanisms spelled out above, though sometimes a little extra attention to detail can be important. Let's now imagine that `AuthorSerializer` looks like this:\n\n```py\nclass AuthorSerializer(FieldsListSerializerMixin, serializers.ModelSerializer):\n\n accounts = AccountSerializer(many=True)\n\n class Meta:\n model = settings.AUTH_USER_MODEL\n fields = ('id', 'username', 'email', 'photo', 'accounts', ...)\n```\n\nNaturally, if `accounts` is sensitive, internal data, you simply might not use _this_ serializer for external API consumption. Of course, that would solve your problem about how to decide whether to serialize `accounts` data -- the supplied serializer would know nothing about that field! But, let's pretend that in our case, `accounts` is safe for public consumption, and _some_ ticketing API calls require it for triaging purposes, whereas others do not. In such a situation, we'll redefine our ViewSet like so:\n\n```py\nclass TicketViewSet(OptimizedQuerySetMixin, viewsets.ReadOnlyModelViewSet):\n\n queryset = Ticket.objects.all()\n serializer_class = TicketSerializer\n\n @data_predicate('author')\n def load_author(self, queryset):\n return queryset.select_related('author')\n\n # Add this extra data_predicate with prefetches `accounts` if and only if\n # the requests promises to use that information\n @data_predicate('author.accounts')\n def load_author_with_accounts(self, queryset):\n return queryset.select_related('author').prefetch_related('author__accounts')\n```\n\nNow, it is up to the client to decide which of the following options (or anything else imaginable) is most appropriate:\n\n```http\n # Includes specified local fields plus all author fields and relationships\n\nGET /api/tickets/?fields=id,title,author\n\n200 OK\n[\n {\n \"id\": 1,\n \"title\": \"This is a ticket\",\n \"author\": {\n \"id\": 5,\n \"username\": \"HomerSimpson\",\n \"accounts\": [\n {\"all_fields\": \"will_be_present\"}\n ]\n }\n }\n]\n```\n\n or\n\n\n ```http\n # Includes specified local fields plus specified author fields and relationships\n\nGET /api/tickets/?fields=id,title,author(username,photo)\n\n200 OK\n[\n {\n \"id\": 1,\n \"title\": \"This is a ticket\",\n \"author\": {\n \"username\": \"HomerSimpson\",\n \"photo\": \"image_url\"\n }\n }\n]\n ```\n\n or\n\n ```http\n# Includes specified local fields plus specified author fields and relationships plus specified accounts fields and relationships\n\nGET /api/tickets/?fields=id,title,author(id,accounts(id,type_of,date))\n\n200 OK\n [\n {\n \"id\": 1,\n \"title\": \"This is a ticket\",\n \"author\": {\n \"id\": 5,\n \"accounts\": [\n {\n \"id\": 8001,\n \"type_of\": \"business\",\n \"date\": \"2018-01-01T12:00:00Z\"\n },\n {\n \"id\": 6500,\n \"type_of\": \"trial\",\n \"date\": \"2017-06-01T12:00:00Z\"\n }\n ]\n }\n }\n]\n ```\n\nIn short, know that as long as the entire chain of Serializers implements the `FieldsListSerializerMixin`, arbitrarily deep nesting of `?fields` declarations will be honored. However, in practice, because relationships are expensive to hydrate, you will probably want to limit that information and control what data you actually load using the `@data_predicate` decorator on ViewSet methods.\n\n\n## Testing\n\n```bash\n$ make tests\n```\n\nor keep them running on change:\n\n```bash\n$ make watch\n```\n\nYou can also use the excellent [tox](http://tox.readthedocs.org/en/latest/) testing tool to run the tests against all supported versions of Python and Django. Install tox globally, and then simply run:\n\n```bash\n$ tox\n```\n\n## Documentation\n\n```bash\n$ make docs\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/jllorencetti/drf-jsonmask", "keywords": "", "license": "BSD", "maintainer": "Craig Labenz, Jo\u00e3o Luiz Lorencetti (Fork author)", "maintainer_email": "jll.linux@gmail.com", "name": "drf-jsonmask", "package_url": "https://pypi.org/project/drf-jsonmask/", "platform": "", "project_url": "https://pypi.org/project/drf-jsonmask/", "project_urls": { "Homepage": "https://github.com/jllorencetti/drf-jsonmask" }, "release_url": "https://pypi.org/project/drf-jsonmask/0.0.1/", "requires_dist": null, "requires_python": "", "summary": "Implements Google's partial response in Django RestFramework (Fork from Zapier's package)", "version": "0.0.1" }, "last_serial": 5711596, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "97eb14842870416621ccf4714f385ad1", "sha256": "7f926b0edc5c40e1f0c5ff2b4ba08bfc6e5791f27622c2cf4e7dc3b027218668" }, "downloads": -1, "filename": "drf-jsonmask-0.0.1.tar.gz", "has_sig": false, "md5_digest": "97eb14842870416621ccf4714f385ad1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8926, "upload_time": "2019-08-21T20:24:37", "url": "https://files.pythonhosted.org/packages/25/4f/83675a5489e6a7661079b610c88ed173555c595377280ecfa61ea8888c13/drf-jsonmask-0.0.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "97eb14842870416621ccf4714f385ad1", "sha256": "7f926b0edc5c40e1f0c5ff2b4ba08bfc6e5791f27622c2cf4e7dc3b027218668" }, "downloads": -1, "filename": "drf-jsonmask-0.0.1.tar.gz", "has_sig": false, "md5_digest": "97eb14842870416621ccf4714f385ad1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8926, "upload_time": "2019-08-21T20:24:37", "url": "https://files.pythonhosted.org/packages/25/4f/83675a5489e6a7661079b610c88ed173555c595377280ecfa61ea8888c13/drf-jsonmask-0.0.1.tar.gz" } ] }