From 222fc528bcb62e2f599e97fb09bf216ff34cf91e Mon Sep 17 00:00:00 2001 From: Alan Crosswell Date: Wed, 21 Mar 2018 18:51:03 -0400 Subject: [PATCH 1/6] WIP: Improvements for pagination, sorting, filtering, and exception handling * Pagination updates: * Improve existing documentation of pagination, mixins * Document LimitOffsetPagination * Describe how to override pagination class query parameter names. * Remove reference to PAGINATE_BY_PARAM which was deprecated in DRF 3.2. * Document SparseFieldsetsMixin * Add new default settings for pagination query parameters and maximum size. * Add combinable mixins for filter and sort query parameters and make MultiplIDMixin combinable. * Exceptions updates: * Document JSON_API_UNIFORM_EXCEPTIONS setting. * handle missing fields exception thrown by new filter and sort Mixins as a 400 error. * Catch all exceptions not caught by DRF and format as JSON API error objects instead of returning HTML error pages. --- CHANGELOG.md | 14 ++++ docs/api.md | 64 +++++++++++++++++- docs/usage.md | 95 +++++++++++++++++++++++++-- rest_framework_json_api/exceptions.py | 47 ++++++++++++- rest_framework_json_api/mixins.py | 72 +++++++++++++++++++- rest_framework_json_api/pagination.py | 17 +++-- 6 files changed, 293 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc2b441f..7d7bf297 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +v2.4.x - [Unreleased] +* Pagination updates: + * Improve existing documentation of pagination, mixins + * Document LimitOffsetPagination + * Describe how to override pagination class query parameter names. + * Remove reference to PAGINATE_BY_PARAM which was deprecated in DRF 3.2. + * Document SparseFieldsetsMixin + * Add new default settings for pagination query parameters and maximum size. +* Add combinable mixins for filter and sort query parameters and make MultiplIDMixin combinable. +* Exceptions updates: + * Document JSON_API_UNIFORM_EXCEPTIONS setting. + * handle missing fields exception thrown by new filter and sort Mixins as a 400 error. + * Catch all exceptions not caught by DRF and format as JSON API error objects instead of returning HTML error pages. + v2.4.0 - Released January 25, 2018 * Add support for Django REST Framework 3.7.x. diff --git a/docs/api.md b/docs/api.md index 86116d5c..760f2421 100644 --- a/docs/api.md +++ b/docs/api.md @@ -4,9 +4,71 @@ ## mixins ### MultipleIDMixin -Add this mixin to a view to override `get_queryset` to automatically filter +Add this mixin to a view to override `get_queryset` to filter records by `ids[]=1&ids[]=2` in URL query params. +For example: +```http request +GET /widgets/?ids[]=123&ids[]=456 +``` + +You might want to consider using the `FilterMixin` where the equivalent example is: +```http request +GET /widgets/?filter[id]=123,456 +``` + +### FilterMixin + +This ViewSet mixin augments `get_queryset` to provide JSON API filter query parameter support +per the [recommendation](http://jsonapi.org/recommendations/#filtering). + +The `filter` syntax is `filter[name1]=list,of,alternative,values&filter[name2]=more,alternatives...` +which can be interpreted as `(name1 in [list,of,alternative,values]) and (name2 in [more,alternatives])` +`name` can be `id` or attributes fields. + +For example: + +```http request +GET /widgets/?filter[name]=can+opener,tap&filter[color]=red +``` + +### SortMixin + +This ViewSet mixin augments `get_queryset` to provide JSON API sort query parameter support +per the [recommendation](http://jsonapi.org/format/#fetching-sorting). + +The `sort` syntax is `sort=-field1,field2,...` + +For example: + +```http request +GET /widgets/?sort=-name,color +``` + +### SparseFieldsetsMixin + +This Serializer mixin implements [sparse fieldsets](http://jsonapi.org/format/#fetching-sparse-fieldsets) +with the `fields[type]=` parameter. It is included by default in the HyperLinkedModelSerializer and +ModelSerializer classes. + +For example: + +```http request +GET /widgets/?fields[widgets]=name +``` + +### IncludedResourcesValidationMixin + +This Serializer mixin implements [included compound documents](http://jsonapi.org/format/#document-compound-documents) +with the `include=` parameter. It is included by default in the HyperLinkedModelSerializer and +ModelSerializer classes. + +For example: + +```http request +GET /widgets/?included=locations +``` + ## rest_framework_json_api.renderers.JSONRenderer The `JSONRenderer` exposes a number of methods that you may override if you need diff --git a/docs/usage.md b/docs/usage.md index 7b0b6cc0..8e1bd3d5 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -34,14 +34,73 @@ REST_FRAMEWORK = { } ``` -If `PAGE_SIZE` is set the renderer will return a `meta` object with +### Pagination settings + +If `REST_FRAMEWORK['PAGE_SIZE']` is set the renderer will return a `meta` object with record count and a `links` object with the next, previous, first, and last links. -Pages can be selected with the `page` GET parameter. The query parameter used to -retrieve the page can be customized by subclassing `PageNumberPagination` and -overriding the `page_query_param`. Page size can be controlled per request via -the `PAGINATE_BY_PARAM` query parameter (`page_size` by default). -#### Performance Testing +#### Subclassing paginators + +The JSON API pagination classes can be subclassed to override settings as +described in the [DRF pagination documentation](http://www.django-rest-framework.org/api-guide/pagination/). + +The default values are shown in these examples: + +```python +from rest_framework_json_api.pagination import PageNumberPagination, LimitOffsetPagination + +class MyPagePagination(PageNumberPagination): + page_query_param = 'page' + page_size_query_param = 'page_size' + max_page_size = 100 + +class MyLimitPagination(LimitOffsetPagination): + offset_query_param = 'page[offset]' + limit_query_param = 'page[limit]' + max_limit = None +``` + +As shown above, pages can be selected with the `page` or `page[limit]` GET query parameter when using +the PageNumberPagination or LimitOffsetPagination class, respectively. + +If you want to use the PageNumberPagination query parameter names shown +as an example in the JSON API [specification](http://jsonapi.org/format/#fetching-pagination), +set them as follows: +```python +class MyPagePagination(PageNumberPagination): + page_query_param = 'page[number]' + page_size_query_param = 'page[size]' +``` + +#### Setting global defaults for pagination + +Set global defaults for the `PageNumberPagination` class with these settings: +- `JSON_API_PAGE_NUMBER_PARAM` sets the name of the page number query parameter (default: "page"). +- `JSON_API_PAGE_SIZE_PARAM` sets the name of the page size query parameter (default: "page_size"). +- `JSON_API_MAX_PAGE_SIZE` sets an upper limit for the page size query parameter (default: 100). + +Set global defaults for the `LimitOffsetPagination` class with these settings: +- `JSON_API_PAGE_OFFSET_PARAM` sets the name of the page offset query parameter (default: "page\[offset\]"). +- `JSON_API_PAGE_LIMIT_PARAM` sets the name of the page limit query parameter (default: "page\[limit\]"). +- `JSON_API_MAX_PAGE_LIMIT` sets an upper limit for the page limit query parameter (default: None). + +If you want to set the default PageNumberPagination query parameter names shown +as an example in the JSON API [specification](http://jsonapi.org/format/#fetching-pagination), +set them as follows: +```python +JSON_API_PAGE_NUMBER_PARAM = 'page[number]' +JSON_API_PAGE_SIZE_PARAM = 'page[size]' +``` + +### Exception handling + +For the `exception_handler` class, if the optional `JSON_API_UNIFORM_EXCEPTIONS` is set to True, +all exceptions will respond with the JSON API [error format](http://jsonapi.org/format/#error-objects). + +When `JSON_API_UNIFORM_EXCEPTIONS` is False (the default), non-JSON API views will respond +with the normal DRF error format. + +### Performance Testing If you are trying to see if your viewsets are configured properly to optimize performance, it is preferable to use `example.utils.BrowsableAPIRendererWithoutForms` instead of the default `BrowsableAPIRenderer` @@ -606,6 +665,30 @@ with 1e18 rows which will likely exhaust any available memory and slow your database to crawl. The prefetch_related case will issue 4 queries, but they will be small and fast queries. + +### Filtering and Sorting views and viewsets + +To add JSON API-style [filtering](http://jsonapi.org/recommendations/#filtering) +or [sorting](http://jsonapi.org/format/#fetching-sorting) query parameters for GET requests, +add the FilterMixin or SortMixin to a view or viewset. For example: + +```python +from djangorestframework_jsonapi.mixins import FilterMixin, SortMixin +from rest_framework_json_api.views import viewsets + +class WidgetViewSet(SortMixin, FilterMixin, viewsets.ModelViewSet): + queryset = Widget.objects.all() + serializer_class = WidgetSerializer +``` + +### Sparse Fieldsets + +The HyperlinkedModelSerializer already includes the mixin for the +`field[type]=attr1,attr2,...` parameter +([sparse fieldsets](http://jsonapi.org/format/#fetching-sparse-fieldsets)). + + +