This commit is contained in:
Asif Saif Uddin (Auvi) 2019-11-06 17:31:11 +06:00
commit b887b14b60
13 changed files with 47 additions and 32 deletions

View File

@ -17,11 +17,16 @@ other cache decorators such as [`cache_page`][page] and
[`vary_on_cookie`][cookie]. [`vary_on_cookie`][cookie].
```python ```python
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.decorators.vary import vary_on_cookie
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework import viewsets from rest_framework import viewsets
class UserViewSet(viewsets.Viewset):
class UserViewSet(viewsets.ViewSet):
# Cache requested url for each user for 2 hours # Cache requested url for each user for 2 hours
@method_decorator(cache_page(60*60*2)) @method_decorator(cache_page(60*60*2))
@ -32,6 +37,7 @@ class UserViewSet(viewsets.Viewset):
} }
return Response(content) return Response(content)
class PostView(APIView): class PostView(APIView):
# Cache page for the requested url # Cache page for the requested url

View File

@ -713,7 +713,7 @@ the coordinate pair:
fields = ['label', 'coordinates'] fields = ['label', 'coordinates']
Note that this example doesn't handle validation. Partly for that reason, in a Note that this example doesn't handle validation. Partly for that reason, in a
real project, the coordinate nesting might be better handled with a nested serialiser real project, the coordinate nesting might be better handled with a nested serializer
using `source='*'`, with two `IntegerField` instances, each with their own `source` using `source='*'`, with two `IntegerField` instances, each with their own `source`
pointing to the relevant field. pointing to the relevant field.
@ -746,7 +746,7 @@ suitable for updating our target object. With `source='*'`, the return from
('y_coordinate', 4), ('y_coordinate', 4),
('x_coordinate', 3)]) ('x_coordinate', 3)])
For completeness lets do the same thing again but with the nested serialiser For completeness lets do the same thing again but with the nested serializer
approach suggested above: approach suggested above:
class NestedCoordinateSerializer(serializers.Serializer): class NestedCoordinateSerializer(serializers.Serializer):
@ -768,14 +768,14 @@ declarations. It's our `NestedCoordinateSerializer` that takes `source='*'`.
Our new `DataPointSerializer` exhibits the same behaviour as the custom field Our new `DataPointSerializer` exhibits the same behaviour as the custom field
approach. approach.
Serialising: Serializing:
>>> out_serializer = DataPointSerializer(instance) >>> out_serializer = DataPointSerializer(instance)
>>> out_serializer.data >>> out_serializer.data
ReturnDict([('label', 'testing'), ReturnDict([('label', 'testing'),
('coordinates', OrderedDict([('x', 1), ('y', 2)]))]) ('coordinates', OrderedDict([('x', 1), ('y', 2)]))])
Deserialising: Deserializing:
>>> in_serializer = DataPointSerializer(data=data) >>> in_serializer = DataPointSerializer(data=data)
>>> in_serializer.is_valid() >>> in_serializer.is_valid()
@ -802,8 +802,8 @@ But we also get the built-in validation for free:
{'x': ['A valid integer is required.'], {'x': ['A valid integer is required.'],
'y': ['A valid integer is required.']})]) 'y': ['A valid integer is required.']})])
For this reason, the nested serialiser approach would be the first to try. You For this reason, the nested serializer approach would be the first to try. You
would use the custom field approach when the nested serialiser becomes infeasible would use the custom field approach when the nested serializer becomes infeasible
or overly complex. or overly complex.

View File

@ -378,10 +378,6 @@ If you need to generic PUT-as-create behavior you may want to include something
The following third party packages provide additional generic view implementations. The following third party packages provide additional generic view implementations.
## Django REST Framework bulk
The [django-rest-framework-bulk package][django-rest-framework-bulk] implements generic view mixins as well as some common concrete generic views to allow to apply bulk operations via API requests.
## Django Rest Multiple Models ## Django Rest Multiple Models
[Django Rest Multiple Models][django-rest-multiple-models] provides a generic view (and mixin) for sending multiple serialized models and/or querysets via a single API request. [Django Rest Multiple Models][django-rest-multiple-models] provides a generic view (and mixin) for sending multiple serialized models and/or querysets via a single API request.
@ -394,5 +390,4 @@ The [django-rest-framework-bulk package][django-rest-framework-bulk] implements
[RetrieveModelMixin]: #retrievemodelmixin [RetrieveModelMixin]: #retrievemodelmixin
[UpdateModelMixin]: #updatemodelmixin [UpdateModelMixin]: #updatemodelmixin
[DestroyModelMixin]: #destroymodelmixin [DestroyModelMixin]: #destroymodelmixin
[django-rest-framework-bulk]: https://github.com/miki725/django-rest-framework-bulk
[django-rest-multiple-models]: https://github.com/MattBroach/DjangoRestMultipleModels [django-rest-multiple-models]: https://github.com/MattBroach/DjangoRestMultipleModels

View File

@ -49,7 +49,7 @@ If a client sends a request with a content-type that cannot be parsed then a `Un
# Content negotiation # Content negotiation
The request exposes some properties that allow you to determine the result of the content negotiation stage. This allows you to implement behaviour such as selecting a different serialisation schemes for different media types. The request exposes some properties that allow you to determine the result of the content negotiation stage. This allows you to implement behaviour such as selecting a different serialization schemes for different media types.
## .accepted_renderer ## .accepted_renderer

View File

@ -115,7 +115,6 @@ The `get_schema_view()` helper takes the following keyword arguments:
* `renderer_classes`: May be used to pass the set of renderer classes that can * `renderer_classes`: May be used to pass the set of renderer classes that can
be used to render the API root endpoint. be used to render the API root endpoint.
## Customizing Schema Generation ## Customizing Schema Generation
You may customize schema generation at the level of the schema as a whole, or You may customize schema generation at the level of the schema as a whole, or
@ -155,7 +154,7 @@ Returns a dictionary that represents the OpenAPI schema:
The `request` argument is optional, and may be used if you want to apply The `request` argument is optional, and may be used if you want to apply
per-user permissions to the resulting schema generation. per-user permissions to the resulting schema generation.
This is a good point to override if you want to customise the generated This is a good point to override if you want to customize the generated
dictionary, for example to add custom dictionary, for example to add custom
[specification extensions][openapi-specification-extensions]. [specification extensions][openapi-specification-extensions].
@ -177,21 +176,20 @@ for each view, allowed method, and path.
**Note**: For basic `APIView` subclasses, default introspection is essentially **Note**: For basic `APIView` subclasses, default introspection is essentially
limited to the URL kwarg path parameters. For `GenericAPIView` limited to the URL kwarg path parameters. For `GenericAPIView`
subclasses, which includes all the provided class based views, `AutoSchema` will subclasses, which includes all the provided class based views, `AutoSchema` will
attempt to introspect serialiser, pagination and filter fields, as well as attempt to introspect serializer, pagination and filter fields, as well as
provide richer path field descriptions. (The key hooks here are the relevant provide richer path field descriptions. (The key hooks here are the relevant
`GenericAPIView` attributes and methods: `get_serializer`, `pagination_class`, `GenericAPIView` attributes and methods: `get_serializer`, `pagination_class`,
`filter_backends` and so on.) `filter_backends` and so on.)
--- ---
In order to customise the operation generation, you should provide an `AutoSchema` subclass, overriding `get_operation()` as you need: In order to customize the operation generation, you should provide an `AutoSchema` subclass, overriding `get_operation()` as you need:
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.schemas.openapi import AutoSchema from rest_framework.schemas.openapi import AutoSchema
class CustomSchema(AutoSchema): class CustomSchema(AutoSchema):
def get_link(...): def get_operation(...):
# Implement custom introspection here (or in other sub-methods) # Implement custom introspection here (or in other sub-methods)
class CustomView(APIView): class CustomView(APIView):
@ -219,4 +217,4 @@ project you may adjust `settings.DEFAULT_SCHEMA_CLASS` appropriately.
[openapi]: https://github.com/OAI/OpenAPI-Specification [openapi]: https://github.com/OAI/OpenAPI-Specification
[openapi-specification-extensions]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#specification-extensions [openapi-specification-extensions]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#specification-extensions
[openapi-operation]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operationObject [openapi-operation]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operationObject

View File

@ -222,11 +222,11 @@ Be sure to upgrade to Python 3 before upgrading to Django REST Framework 3.10.
def perform_create(self, serializer): def perform_create(self, serializer):
serializer.save(owner=self.request.user) serializer.save(owner=self.request.user)
Alternatively you may override `save()` or `create()` or `update()` on the serialiser as appropriate. Alternatively you may override `save()` or `create()` or `update()` on the serializer as appropriate.
* Correct allow_null behaviour when required=False [#5888][gh5888] * Correct allow_null behaviour when required=False [#5888][gh5888]
Without an explicit `default`, `allow_null` implies a default of `null` for outgoing serialisation. Previously such Without an explicit `default`, `allow_null` implies a default of `null` for outgoing serialization. Previously such
fields were being skipped when read-only or otherwise not required. fields were being skipped when read-only or otherwise not required.
**Possible backwards compatibility break** if you were relying on such fields being excluded from the outgoing **Possible backwards compatibility break** if you were relying on such fields being excluded from the outgoing
@ -464,7 +464,7 @@ Be sure to upgrade to Python 3 before upgrading to Django REST Framework 3.10.
* Deprecated `exclude_from_schema` on `APIView` and `api_view` decorator. Set `schema = None` or `@schema(None)` as appropriate. [#5422][gh5422] * Deprecated `exclude_from_schema` on `APIView` and `api_view` decorator. Set `schema = None` or `@schema(None)` as appropriate. [#5422][gh5422]
* Timezone-aware `DateTimeField`s now respect active or default `timezone` during serialization, instead of always using UTC. [#5435][gh5435] * Timezone-aware `DateTimeField`s now respect active or default `timezone` during serialization, instead of always using UTC. [#5435][gh5435]
Resolves inconsistency whereby instances were serialised with supplied datetime for `create` but UTC for `retrieve`. [#3732][gh3732] Resolves inconsistency whereby instances were serialized with supplied datetime for `create` but UTC for `retrieve`. [#3732][gh3732]
**Possible backwards compatibility break** if you were relying on datetime strings being UTC. Have client interpret datetimes or [set default or active timezone (docs)][djangodocs-set-timezone] to UTC if needed. **Possible backwards compatibility break** if you were relying on datetime strings being UTC. Have client interpret datetimes or [set default or active timezone (docs)][djangodocs-set-timezone] to UTC if needed.

View File

@ -222,7 +222,6 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
### Views ### Views
* [djangorestframework-bulk][djangorestframework-bulk] - Implements generic view mixins as well as some common concrete generic views to allow to apply bulk operations via API requests.
* [django-rest-multiple-models][django-rest-multiple-models] - Provides a generic view (and mixin) for sending multiple serialized models and/or querysets via a single API request. * [django-rest-multiple-models][django-rest-multiple-models] - Provides a generic view (and mixin) for sending multiple serialized models and/or querysets via a single API request.
### Routers ### Routers
@ -254,7 +253,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
### Misc ### Misc
* [cookiecutter-django-rest][cookiecutter-django-rest] - A cookiecutter template that takes care of the setup and configuration so you can focus on making your REST apis awesome. * [cookiecutter-django-rest][cookiecutter-django-rest] - A cookiecutter template that takes care of the setup and configuration so you can focus on making your REST apis awesome.
* [djangorestrelationalhyperlink][djangorestrelationalhyperlink] - A hyperlinked serialiser that can can be used to alter relationships via hyperlinks, but otherwise like a hyperlink model serializer. * [djangorestrelationalhyperlink][djangorestrelationalhyperlink] - A hyperlinked serializer that can can be used to alter relationships via hyperlinks, but otherwise like a hyperlink model serializer.
* [django-rest-swagger][django-rest-swagger] - An API documentation generator for Swagger UI. * [django-rest-swagger][django-rest-swagger] - An API documentation generator for Swagger UI.
* [django-rest-framework-proxy][django-rest-framework-proxy] - Proxy to redirect incoming request to another API server. * [django-rest-framework-proxy][django-rest-framework-proxy] - Proxy to redirect incoming request to another API server.
* [gaiarestframework][gaiarestframework] - Utils for django-rest-framework * [gaiarestframework][gaiarestframework] - Utils for django-rest-framework
@ -305,7 +304,6 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
[djangorestframework-hstore]: https://github.com/djangonauts/django-rest-framework-hstore [djangorestframework-hstore]: https://github.com/djangonauts/django-rest-framework-hstore
[drf-compound-fields]: https://github.com/estebistec/drf-compound-fields [drf-compound-fields]: https://github.com/estebistec/drf-compound-fields
[django-extra-fields]: https://github.com/Hipo/drf-extra-fields [django-extra-fields]: https://github.com/Hipo/drf-extra-fields
[djangorestframework-bulk]: https://github.com/miki725/django-rest-framework-bulk
[django-rest-multiple-models]: https://github.com/MattBroach/DjangoRestMultipleModels [django-rest-multiple-models]: https://github.com/MattBroach/DjangoRestMultipleModels
[drf-nested-routers]: https://github.com/alanjds/drf-nested-routers [drf-nested-routers]: https://github.com/alanjds/drf-nested-routers
[wq.db.rest]: https://wq.io/docs/about-rest [wq.db.rest]: https://wq.io/docs/about-rest

View File

@ -191,7 +191,7 @@ each view, allowed method and path.)
**Note**: For basic `APIView` subclasses, default introspection is essentially **Note**: For basic `APIView` subclasses, default introspection is essentially
limited to the URL kwarg path parameters. For `GenericAPIView` limited to the URL kwarg path parameters. For `GenericAPIView`
subclasses, which includes all the provided class based views, `AutoSchema` will subclasses, which includes all the provided class based views, `AutoSchema` will
attempt to introspect serialiser, pagination and filter fields, as well as attempt to introspect serializer, pagination and filter fields, as well as
provide richer path field descriptions. (The key hooks here are the relevant provide richer path field descriptions. (The key hooks here are the relevant
`GenericAPIView` attributes and methods: `get_serializer`, `pagination_class`, `GenericAPIView` attributes and methods: `get_serializer`, `pagination_class`,
`filter_backends` and so on.) `filter_backends` and so on.)

View File

@ -10,7 +10,7 @@ ______ _____ _____ _____ __
__title__ = 'Django REST framework' __title__ = 'Django REST framework'
__version__ = '3.10.3' __version__ = '3.10.3'
__author__ = 'Tom Christie' __author__ = 'Tom Christie'
__license__ = 'BSD 2-Clause' __license__ = 'BSD 3-Clause'
__copyright__ = 'Copyright 2011-2019 Encode OSS Ltd' __copyright__ = 'Copyright 2011-2019 Encode OSS Ltd'
# Version synonym # Version synonym

View File

@ -209,7 +209,7 @@ class AutoSchema(ViewInspector):
if not is_list_view(path, method, view): if not is_list_view(path, method, view):
return [] return []
paginator = self._get_pagninator() paginator = self._get_paginator()
if not paginator: if not paginator:
return [] return []
@ -344,6 +344,7 @@ class AutoSchema(ViewInspector):
serializers.BooleanField: 'boolean', serializers.BooleanField: 'boolean',
serializers.JSONField: 'object', serializers.JSONField: 'object',
serializers.DictField: 'object', serializers.DictField: 'object',
serializers.HStoreField: 'object',
} }
return {'type': FIELD_CLASS_SCHEMA_TYPE.get(field.__class__, 'string')} return {'type': FIELD_CLASS_SCHEMA_TYPE.get(field.__class__, 'string')}
@ -429,7 +430,7 @@ class AutoSchema(ViewInspector):
schema['maximum'] = int(digits * '9') + 1 schema['maximum'] = int(digits * '9') + 1
schema['minimum'] = -schema['maximum'] schema['minimum'] = -schema['maximum']
def _get_pagninator(self): def _get_paginator(self):
pagination_class = getattr(self.view, 'pagination_class', None) pagination_class = getattr(self.view, 'pagination_class', None)
if pagination_class: if pagination_class:
return pagination_class() return pagination_class()
@ -502,7 +503,7 @@ class AutoSchema(ViewInspector):
'type': 'array', 'type': 'array',
'items': item_schema, 'items': item_schema,
} }
paginator = self._get_pagninator() paginator = self._get_paginator()
if paginator: if paginator:
response_schema = paginator.get_paginated_response_schema(response_schema) response_schema = paginator.get_paginated_response_schema(response_schema)
else: else:

View File

@ -462,6 +462,22 @@ class TestOperationIntrospection(TestCase):
assert properties['date']['format'] == 'date' assert properties['date']['format'] == 'date'
assert properties['datetime']['format'] == 'date-time' assert properties['datetime']['format'] == 'date-time'
def test_serializer_hstorefield(self):
path = '/'
method = 'GET'
view = create_view(
views.ExampleGenericAPIView,
method,
create_request(path),
)
inspector = AutoSchema()
inspector.view = view
responses = inspector._get_responses(path, method)
response_schema = responses['200']['content']['application/json']['schema']
properties = response_schema['items']['properties']
assert properties['hstore']['type'] == 'object'
def test_serializer_validators(self): def test_serializer_validators(self):
path = '/' path = '/'
method = 'GET' method = 'GET'

View File

@ -33,6 +33,7 @@ class ExampleDetailView(APIView):
class ExampleSerializer(serializers.Serializer): class ExampleSerializer(serializers.Serializer):
date = serializers.DateField() date = serializers.DateField()
datetime = serializers.DateTimeField() datetime = serializers.DateTimeField()
hstore = serializers.HStoreField()
class ExampleGenericAPIView(generics.GenericAPIView): class ExampleGenericAPIView(generics.GenericAPIView):

View File

@ -555,7 +555,7 @@ class TestDefaultOutput:
bar = serializers.CharField(source='foo.bar', allow_null=True) bar = serializers.CharField(source='foo.bar', allow_null=True)
optional = serializers.CharField(required=False, allow_null=True) optional = serializers.CharField(required=False, allow_null=True)
# allow_null=True should imply default=None when serialising: # allow_null=True should imply default=None when serializing:
assert Serializer({'foo': None}).data == {'foo': None, 'bar': None, 'optional': None, } assert Serializer({'foo': None}).data == {'foo': None, 'bar': None, 'optional': None, }