diff --git a/.gitignore b/.gitignore index e9222c2da..41768084c 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ /*.egg-info/ /env/ MANIFEST +coverage.* !.gitignore !.travis.yml diff --git a/.travis.yml b/.travis.yml index cf3cddfec..cd87dd339 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,31 +1,51 @@ language: python python: - - "3.5" + - "2.7" + - "3.4" + - "3.5" sudo: false env: - - TOX_ENV=py27-lint - - TOX_ENV=py27-docs - - TOX_ENV=py35-django19 - - TOX_ENV=py34-django19 - - TOX_ENV=py27-django19 - - TOX_ENV=py34-django18 - - TOX_ENV=py33-django18 - - TOX_ENV=py32-django18 - - TOX_ENV=py27-django18 + - DJANGO=1.8 + - DJANGO=1.9 + - DJANGO=1.10 + - DJANGO=1.11 + - DJANGO=master matrix: - fast_finish: true + fast_finish: true + include: + - python: "3.6" + env: DJANGO=master + - python: "3.6" + env: DJANGO=1.11 + - python: "3.3" + env: DJANGO=1.8 + - python: "2.7" + env: TOXENV="lint" + - python: "2.7" + env: TOXENV="docs" + exclude: + - python: "2.7" + env: DJANGO=master + - python: "3.4" + env: DJANGO=master + + allow_failures: + - env: DJANGO=master + - env: DJANGO=1.11 install: - # Virtualenv < 14 is required to keep the Python 3.2 builds running. - - pip install tox "virtualenv<14" + - pip install tox tox-travis script: - - tox -e $TOX_ENV + - tox after_success: - pip install codecov - - codecov -e TOX_ENV + - codecov -e TOXENV,DJANGO + +notifications: + email: false diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 99479acdf..415e42ac0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -61,6 +61,7 @@ To run the tests, clone the repository, and then: # Setup the virtual environment virtualenv env source env/bin/activate + pip install django pip install -r requirements.txt # Run the tests @@ -200,7 +201,7 @@ If you want to draw attention to a note or warning, use a pair of enclosing line [issues]: https://github.com/tomchristie/django-rest-framework/issues?state=open [pep-8]: http://www.python.org/dev/peps/pep-0008/ [pull-requests]: https://help.github.com/articles/using-pull-requests -[tox]: http://tox.readthedocs.org/en/latest/ +[tox]: https://tox.readthedocs.io/en/latest/ [markdown]: http://daringfireball.net/projects/markdown/basics [docs]: https://github.com/tomchristie/django-rest-framework/tree/master/docs [mou]: http://mouapp.com/ diff --git a/README.md b/README.md index 9025e91b4..609f99184 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![build-status-image]][travis] [![coverage-status-image]][codecov] [![pypi-version]][pypi] +[![Gitter](https://badges.gitter.im/tomchristie/django-rest-framework.svg)](https://gitter.im/tomchristie/django-rest-framework?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) **Awesome web-browsable Web APIs.** @@ -10,9 +11,24 @@ Full documentation for the project is available at [http://www.django-rest-frame --- -**Note**: We have now released Django REST framework 3.3. For older codebases you may want to refer to the version 2.4.4 [source code][2.4-code], and [documentation][2.4-docs]. +# Funding -For more details see the 3.3 [announcement][3.3-announcement] and [release notes][3.3-release-notes]. +REST framework is a *collaboratively funded project*. If you use +REST framework commercially we strongly encourage you to invest in its +continued development by **[signing up for a paid plan][funding]**. + +The initial aim is to provide a single full-time position on REST framework. +*Every single sign-up makes a significant impact towards making that possible.* + +

+ + + + + +

+ +*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), [Machinalis](https://hello.machinalis.co.uk/), and [Rollbar](https://rollbar.com).* --- @@ -37,7 +53,7 @@ There is a live example API for testing purposes, [available here][sandbox]. # Requirements * Python (2.7, 3.2, 3.3, 3.4, 3.5) -* Django (1.8, 1.9) +* Django (1.8, 1.9, 1.10) # Installation @@ -155,7 +171,7 @@ You may also want to [follow the author on Twitter][twitter]. # Security -If you believe you’ve found something in Django REST framework which has security implications, please **do not raise the issue in a public forum**. +If you believe you've found something in Django REST framework which has security implications, please **do not raise the issue in a public forum**. Send a description of the issue via email to [rest-framework-security@googlegroups.com][security-mail]. The project maintainers will then work with you to resolve any issues where required, prior to any public disclosure. @@ -169,6 +185,9 @@ Send a description of the issue via email to [rest-framework-security@googlegrou [group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework [sandbox]: http://restframework.herokuapp.com/ +[funding]: https://fund.django-rest-framework.org/topics/funding/ +[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors + [oauth1-section]: http://www.django-rest-framework.org/api-guide/authentication/#django-rest-framework-oauth [oauth2-section]: http://www.django-rest-framework.org/api-guide/authentication/#django-oauth-toolkit [serializer-section]: http://www.django-rest-framework.org/api-guide/serializers/#serializers @@ -183,7 +202,3 @@ Send a description of the issue via email to [rest-framework-security@googlegrou [docs]: http://www.django-rest-framework.org/ [security-mail]: mailto:rest-framework-security@googlegroups.com -[2.4-code]: https://github.com/tomchristie/django-rest-framework/tree/version-2.4.x -[2.4-docs]: http://tomchristie.github.io/rest-framework-2-docs/ -[3.3-announcement]: http://www.django-rest-framework.org/topics/3.3-announcement/ -[3.3-release-notes]: http://www.django-rest-framework.org/topics/release-notes/#33x-series diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 3d1cfd1e8..4a01188f3 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -44,7 +44,7 @@ The default authentication schemes may be set globally, using the `DEFAULT_AUTHE } You can also set the authentication scheme on a per-view or per-viewset basis, -using the `APIView` class based views. +using the `APIView` class-based views. from rest_framework.authentication import SessionAuthentication, BasicAuthentication from rest_framework.permissions import IsAuthenticated @@ -128,11 +128,10 @@ To use the `TokenAuthentication` scheme you'll need to [configure the authentica --- -**Note:** Make sure to run `manage.py syncdb` after changing your settings. The `rest_framework.authtoken` app provides both Django (from v1.7) and South database migrations. See [Schema migrations](#schema-migrations) below. +**Note:** Make sure to run `manage.py migrate` after changing your settings. The `rest_framework.authtoken` app provides Django database migrations. --- - You'll also need to create tokens for your users. from rest_framework.authtoken.models import Token @@ -144,10 +143,12 @@ For clients to authenticate, the token key should be included in the `Authorizat Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b +**Note:** If you want to use a different keyword in the header, such as `Bearer`, simply subclass `TokenAuthentication` and set the `keyword` class variable. + If successfully authenticated, `TokenAuthentication` provides the following credentials. * `request.user` will be a Django `User` instance. -* `request.auth` will be a `rest_framework.authtoken.models.BasicToken` instance. +* `request.auth` will be a `rest_framework.authtoken.models.Token` instance. Unauthenticated responses that are denied permission will result in an `HTTP 401 Unauthorized` response with an appropriate WWW-Authenticate header. For example: @@ -206,6 +207,10 @@ The `obtain_auth_token` view will return a JSON response when valid `username` a Note that the default `obtain_auth_token` view explicitly uses JSON requests and responses, rather than using default renderer and parser classes in your settings. If you need a customized version of the `obtain_auth_token` view, you can do so by overriding the `ObtainAuthToken` view class, and using that in your url conf instead. +By default there are no permissions or throttling applied to the `obtain_auth_token` view. If you do wish to apply throttling you'll need to override the view class, +and include them using the `throttle_classes` attribute. + + ##### With Django admin It is also possible to create Tokens manually through admin interface. In case you are using a large user base, we recommend that you monkey patch the `TokenAdmin` class to customize it to your needs, more specifically by declaring the `user` field as `raw_field`. @@ -217,38 +222,6 @@ It is also possible to create Tokens manually through admin interface. In case y TokenAdmin.raw_id_fields = ('user',) -#### Schema migrations - -The `rest_framework.authtoken` app includes both Django native migrations (for Django versions >1.7) and South migrations (for Django versions <1.7) that will create the authtoken table. - ----- - -**Note**: From REST Framework v2.4.0 using South with Django <1.7 requires upgrading South v1.0+ - ----- - - -If you're using a [custom user model][custom-user-model] you'll need to make sure that any initial migration that creates the user table runs before the authtoken table is created. - -You can do so by inserting a `needed_by` attribute in your user migration: - - class Migration: - - needed_by = ( - ('authtoken', '0001_initial'), - ) - - def forwards(self): - ... - -For more details, see the [south documentation on dependencies][south-dependencies]. - -Also note that if you're using a `post_save` signal to create tokens, then the first time you create the database tables, you'll need to ensure any migrations are run prior to creating any superusers. For example: - - python manage.py syncdb --noinput # Won't create a superuser just yet, due to `--noinput`. - python manage.py migrate - python manage.py createsuperuser - ## SessionAuthentication This authentication scheme uses Django's default session backend for authentication. Session authentication is appropriate for AJAX clients that are running in the same session context as your website. @@ -390,11 +363,9 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a [oauth]: http://oauth.net/2/ [permission]: permissions.md [throttling]: throttling.md -[csrf-ajax]: https://docs.djangoproject.com/en/dev/ref/csrf/#ajax +[csrf-ajax]: https://docs.djangoproject.com/en/stable/ref/csrf/#ajax [mod_wsgi_official]: http://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIPassAuthorization -[custom-user-model]: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#specifying-a-custom-user-model -[south-dependencies]: http://south.readthedocs.org/en/latest/dependencies.html -[django-oauth-toolkit-getting-started]: https://django-oauth-toolkit.readthedocs.org/en/latest/rest-framework/getting_started.html +[django-oauth-toolkit-getting-started]: https://django-oauth-toolkit.readthedocs.io/en/latest/rest-framework/getting_started.html [django-rest-framework-oauth]: http://jpadilla.github.io/django-rest-framework-oauth/ [django-rest-framework-oauth-authentication]: http://jpadilla.github.io/django-rest-framework-oauth/authentication/ [django-rest-framework-oauth-permissions]: http://jpadilla.github.io/django-rest-framework-oauth/permissions/ @@ -403,7 +374,7 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a [oauth-1.0a]: http://oauth.net/core/1.0a [django-oauth-plus]: http://code.larlet.fr/django-oauth-plus [django-oauth2-provider]: https://github.com/caffeinehit/django-oauth2-provider -[django-oauth2-provider-docs]: https://django-oauth2-provider.readthedocs.org/en/latest/ +[django-oauth2-provider-docs]: https://django-oauth2-provider.readthedocs.io/en/latest/ [rfc6749]: http://tools.ietf.org/html/rfc6749 [django-oauth-toolkit]: https://github.com/evonove/django-oauth-toolkit [evonove]: https://github.com/evonove/ @@ -417,9 +388,9 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a [djangorestframework-httpsignature]: https://github.com/etoccalino/django-rest-framework-httpsignature [amazon-http-signature]: http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html [http-signature-ietf-draft]: https://datatracker.ietf.org/doc/draft-cavage-http-signatures/ -[hawkrest]: http://hawkrest.readthedocs.org/en/latest/ +[hawkrest]: https://hawkrest.readthedocs.io/en/latest/ [hawk]: https://github.com/hueniverse/hawk -[mohawk]: http://mohawk.readthedocs.org/en/latest/ +[mohawk]: https://mohawk.readthedocs.io/en/latest/ [mac]: http://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token-05 [djoser]: https://github.com/sunscrapers/djoser [django-rest-auth]: https://github.com/Tivix/django-rest-auth diff --git a/docs/api-guide/content-negotiation.md b/docs/api-guide/content-negotiation.md index bc3b09fb7..bd408feba 100644 --- a/docs/api-guide/content-negotiation.md +++ b/docs/api-guide/content-negotiation.md @@ -77,7 +77,7 @@ The default content negotiation class may be set globally, using the `DEFAULT_CO 'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'myapp.negotiation.IgnoreClientContentNegotiation', } -You can also set the content negotiation used for an individual view, or viewset, using the `APIView` class based views. +You can also set the content negotiation used for an individual view, or viewset, using the `APIView` class-based views. from myapp.negotiation import IgnoreClientContentNegotiation from rest_framework.response import Response diff --git a/docs/api-guide/exceptions.md b/docs/api-guide/exceptions.md index 3e4b3e8be..03f16222d 100644 --- a/docs/api-guide/exceptions.md +++ b/docs/api-guide/exceptions.md @@ -47,7 +47,7 @@ Any example validation error might look like this: You can implement custom exception handling by creating a handler function that converts exceptions raised in your API views into response objects. This allows you to control the style of error responses used by your API. -The function must take a pair of arguments, this first is the exception to be handled, and the second is a dictionary containing any extra context such as the view currently being handled. The exception handler function should either return a `Response` object, or return `None` if the exception cannot be handled. If the handler returns `None` then the exception will be re-raised and Django will return a standard HTTP 500 'server error' response. +The function must take a pair of arguments, the first is the exception to be handled, and the second is a dictionary containing any extra context such as the view currently being handled. The exception handler function should either return a `Response` object, or return `None` if the exception cannot be handled. If the handler returns `None` then the exception will be re-raised and Django will return a standard HTTP 500 'server error' response. For example, you might want to ensure that all error responses include the HTTP status code in the body of the response, like so: @@ -98,7 +98,7 @@ Note that the exception handler will only be called for responses generated by r The **base class** for all exceptions raised inside an `APIView` class or `@api_view`. -To provide a custom exception, subclass `APIException` and set the `.status_code` and `.default_detail` properties on the class. +To provide a custom exception, subclass `APIException` and set the `.status_code`, `.default_detail`, and `default_code` attributes on the class. For example, if your API relies on a third party service that may sometimes be unreachable, you might want to implement an exception for the "503 Service Unavailable" HTTP response code. You could do this like so: @@ -107,10 +107,42 @@ For example, if your API relies on a third party service that may sometimes be u class ServiceUnavailable(APIException): status_code = 503 default_detail = 'Service temporarily unavailable, try again later.' + default_code = 'service_unavailable' + +#### Inspecting API exceptions + +There are a number of different properties available for inspecting the status +of an API exception. You can use these to build custom exception handling +for your project. + +The available attributes and methods are: + +* `.detail` - Return the textual description of the error. +* `.get_codes()` - Return the code identifier of the error. +* `.get_full_details()` - Return both the textual description and the code identifier. + +In most cases the error detail will be a simple item: + + >>> print(exc.detail) + You do not have permission to perform this action. + >>> print(exc.get_codes()) + permission_denied + >>> print(exc.get_full_details()) + {'message':'You do not have permission to perform this action.','code':'permission_denied'} + +In the case of validation errors the error detail will be either a list or +dictionary of items: + + >>> print(exc.detail) + {"name":"This field is required.","age":"A valid integer is required."} + >>> print(exc.get_codes()) + {"name":"required","age":"invalid"} + >>> print(exc.get_full_details()) + {"name":{"message":"This field is required.","code":"required"},"age":{"message":"A valid integer is required.","code":"invalid"}} ## ParseError -**Signature:** `ParseError(detail=None)` +**Signature:** `ParseError(detail=None, code=None)` Raised if the request contains malformed data when accessing `request.data`. @@ -118,7 +150,7 @@ By default this exception results in a response with the HTTP status code "400 B ## AuthenticationFailed -**Signature:** `AuthenticationFailed(detail=None)` +**Signature:** `AuthenticationFailed(detail=None, code=None)` Raised when an incoming request includes incorrect authentication. @@ -126,7 +158,7 @@ By default this exception results in a response with the HTTP status code "401 U ## NotAuthenticated -**Signature:** `NotAuthenticated(detail=None)` +**Signature:** `NotAuthenticated(detail=None, code=None)` Raised when an unauthenticated request fails the permission checks. @@ -134,7 +166,7 @@ By default this exception results in a response with the HTTP status code "401 U ## PermissionDenied -**Signature:** `PermissionDenied(detail=None)` +**Signature:** `PermissionDenied(detail=None, code=None)` Raised when an authenticated request fails the permission checks. @@ -142,7 +174,7 @@ By default this exception results in a response with the HTTP status code "403 F ## NotFound -**Signature:** `NotFound(detail=None)` +**Signature:** `NotFound(detail=None, code=None)` Raised when a resource does not exists at the given URL. This exception is equivalent to the standard `Http404` Django exception. @@ -150,7 +182,7 @@ By default this exception results in a response with the HTTP status code "404 N ## MethodNotAllowed -**Signature:** `MethodNotAllowed(method, detail=None)` +**Signature:** `MethodNotAllowed(method, detail=None, code=None)` Raised when an incoming request occurs that does not map to a handler method on the view. @@ -158,7 +190,7 @@ By default this exception results in a response with the HTTP status code "405 M ## NotAcceptable -**Signature:** `NotAcceptable(detail=None)` +**Signature:** `NotAcceptable(detail=None, code=None)` Raised when an incoming request occurs with an `Accept` header that cannot be satisfied by any of the available renderers. @@ -166,7 +198,7 @@ By default this exception results in a response with the HTTP status code "406 N ## UnsupportedMediaType -**Signature:** `UnsupportedMediaType(media_type, detail=None)` +**Signature:** `UnsupportedMediaType(media_type, detail=None, code=None)` Raised if there are no parsers that can handle the content type of the request data when accessing `request.data`. @@ -174,7 +206,7 @@ By default this exception results in a response with the HTTP status code "415 U ## Throttled -**Signature:** `Throttled(wait=None, detail=None)` +**Signature:** `Throttled(wait=None, detail=None, code=None)` Raised when an incoming request fails the throttling checks. @@ -182,7 +214,7 @@ By default this exception results in a response with the HTTP status code "429 T ## ValidationError -**Signature:** `ValidationError(detail)` +**Signature:** `ValidationError(detail, code=None)` The `ValidationError` exception is slightly different from the other `APIException` classes: diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 0409f9a6c..22ce7dd77 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -49,7 +49,9 @@ Defaults to `False` ### `default` -If set, this gives the default value that will be used for the field if no input value is supplied. If not set the default behavior is to not populate the attribute at all. +If set, this gives the default value that will be used for the field if no input value is supplied. If not set the default behaviour is to not populate the attribute at all. + +The `default` is not applied during partial update operations. In the partial update case only fields that are provided in the incoming data will have a validated value returned. May be set to a function or other callable, in which case the value will be evaluated each time it is used. When called, it will receive no arguments. If the callable has a `set_context` method, that will be called each time before getting the value with the field instance as only argument. This works the same way as for [validators](validators.md#using-set_context). @@ -259,11 +261,12 @@ Corresponds to `django.db.models.fields.DecimalField`. **Signature**: `DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None)` -- `max_digits` The maximum number of digits allowed in the number. Note that this number must be greater than or equal to decimal_places. +- `max_digits` The maximum number of digits allowed in the number. It must be either `None` or an integer greater than or equal to `decimal_places`. - `decimal_places` The number of decimal places to store with the number. -- `coerce_to_string` Set to `True` if string values should be returned for the representation, or `False` if `Decimal` objects should be returned. Defaults to the same value as the `COERCE_DECIMAL_TO_STRING` settings key, which will be `True` unless overridden. If `Decimal` objects are returned by the serializer, then the final output format will be determined by the renderer. +- `coerce_to_string` Set to `True` if string values should be returned for the representation, or `False` if `Decimal` objects should be returned. Defaults to the same value as the `COERCE_DECIMAL_TO_STRING` settings key, which will be `True` unless overridden. If `Decimal` objects are returned by the serializer, then the final output format will be determined by the renderer. Note that setting `localize` will force the value to `True`. - `max_value` Validate that the number provided is no greater than this value. - `min_value` Validate that the number provided is no less than this value. +- `localize` Set to `True` to enable localization of input and output based on the current locale. This will also force `coerce_to_string` to `True`. Defaults to `False`. Note that data formatting is enabled if you have set `USE_L10N=True` in your settings file. #### Example usage @@ -289,9 +292,9 @@ A date and time representation. Corresponds to `django.db.models.fields.DateTimeField`. -**Signature:** `DateTimeField(format=None, input_formats=None)` +**Signature:** `DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)` -* `format` - A string representing the output format. If not specified, this defaults to the same value as the `DATETIME_FORMAT` settings key, which will be `'iso-8601'` unless set. Setting to a format string indicates that `to_representation` return values should be coerced to string output. Format strings are described below. Setting this value to `None` indicates that Python `datetime` objects should be returned by `to_representation`. In this case the datetime encoding will be determined by the renderer. +* `format` - A string representing the output format. If not specified, this defaults to the same value as the `DATETIME_FORMAT` settings key, which will be `'iso-8601'` unless set. Setting to a format string indicates that `to_representation` return values should be coerced to string output. Format strings are described below. Setting this value to `None` indicates that Python `datetime` objects should be returned by `to_representation`. In this case the datetime encoding will be determined by the renderer. * `input_formats` - A list of strings representing the input formats which may be used to parse the date. If not specified, the `DATETIME_INPUT_FORMATS` setting will be used, which defaults to `['iso-8601']`. #### `DateTimeField` format strings. @@ -320,7 +323,7 @@ A date representation. Corresponds to `django.db.models.fields.DateField` -**Signature:** `DateField(format=None, input_formats=None)` +**Signature:** `DateField(format=api_settings.DATE_FORMAT, input_formats=None)` * `format` - A string representing the output format. If not specified, this defaults to the same value as the `DATE_FORMAT` settings key, which will be `'iso-8601'` unless set. Setting to a format string indicates that `to_representation` return values should be coerced to string output. Format strings are described below. Setting this value to `None` indicates that Python `date` objects should be returned by `to_representation`. In this case the date encoding will be determined by the renderer. * `input_formats` - A list of strings representing the input formats which may be used to parse the date. If not specified, the `DATE_INPUT_FORMATS` setting will be used, which defaults to `['iso-8601']`. @@ -335,7 +338,7 @@ A time representation. Corresponds to `django.db.models.fields.TimeField` -**Signature:** `TimeField(format=None, input_formats=None)` +**Signature:** `TimeField(format=api_settings.TIME_FORMAT, input_formats=None)` * `format` - A string representing the output format. If not specified, this defaults to the same value as the `TIME_FORMAT` settings key, which will be `'iso-8601'` unless set. Setting to a format string indicates that `to_representation` return values should be coerced to string output. Format strings are described below. Setting this value to `None` indicates that Python `time` objects should be returned by `to_representation`. In this case the time encoding will be determined by the renderer. * `input_formats` - A list of strings representing the input formats which may be used to parse the date. If not specified, the `TIME_INPUT_FORMATS` setting will be used, which defaults to `['iso-8601']`. @@ -431,9 +434,11 @@ Requires either the `Pillow` package or `PIL` package. The `Pillow` package is A field class that validates a list of objects. -**Signature**: `ListField(child)` +**Signature**: `ListField(child, min_length=None, max_length=None)` - `child` - A field instance that should be used for validating the objects in the list. If this argument is not provided then objects in the list will not be validated. +- `min_length` - Validates that the list contains no fewer than this number of elements. +- `max_length` - Validates that the list contains no more than this number of elements. For example, to validate a list of integers you might use something like the following: @@ -485,7 +490,7 @@ This field is used by default with `ModelSerializer` when including field names **Signature**: `ReadOnlyField()` -For example, is `has_expired` was a property on the `Account` model, then the following serializer would automatically generate it as a `ReadOnlyField`: +For example, if `has_expired` was a property on the `Account` model, then the following serializer would automatically generate it as a `ReadOnlyField`: class AccountSerializer(serializers.ModelSerializer): class Meta: @@ -623,7 +628,6 @@ The `.fail()` method is a shortcut for raising `ValidationError` that takes a me def to_internal_value(self, data): if not isinstance(data, six.text_type): - msg = 'Incorrect type. Expected a string, but got %s' self.fail('incorrect_type', input_type=type(data).__name__) if not re.match(r'^rgb\([0-9]+,[0-9]+,[0-9]+\)$', data): @@ -663,14 +667,14 @@ The [django-rest-framework-gis][django-rest-framework-gis] package provides geog The [django-rest-framework-hstore][django-rest-framework-hstore] package provides an `HStoreField` to support [django-hstore][django-hstore] `DictionaryField` model field. -[cite]: https://docs.djangoproject.com/en/dev/ref/forms/api/#django.forms.Form.cleaned_data +[cite]: https://docs.djangoproject.com/en/stable/ref/forms/api/#django.forms.Form.cleaned_data [html-and-forms]: ../topics/html-and-forms.md -[FILE_UPLOAD_HANDLERS]: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FILE_UPLOAD_HANDLERS +[FILE_UPLOAD_HANDLERS]: https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-FILE_UPLOAD_HANDLERS [ecma262]: http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15 -[strftime]: http://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior -[django-widgets]: https://docs.djangoproject.com/en/dev/ref/forms/widgets/ +[strftime]: https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior +[django-widgets]: https://docs.djangoproject.com/en/stable/ref/forms/widgets/ [iso8601]: http://www.w3.org/TR/NOTE-datetime -[drf-compound-fields]: http://drf-compound-fields.readthedocs.org +[drf-compound-fields]: https://drf-compound-fields.readthedocs.io [drf-extra-fields]: https://github.com/Hipo/drf-extra-fields [djangorestframework-recursive]: https://github.com/heywbj/django-rest-framework-recursive [django-rest-framework-gis]: https://github.com/djangonauts/django-rest-framework-gis diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index c32300783..8a23a2ea3 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -89,24 +89,24 @@ Generic filters can also present themselves as HTML controls in the browsable AP ## Setting filter backends -The default filter backends may be set globally, using the `DEFAULT_FILTER_BACKENDS` setting. For example. +The default filter backends may be set globally, using the `DEFAULT_FILTER_BACKENDS` setting. For example. REST_FRAMEWORK = { - 'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',) + 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',) } You can also set the filter backends on a per-view, or per-viewset basis, -using the `GenericAPIView` class based views. +using the `GenericAPIView` class-based views. + import django_filters.rest_framework from django.contrib.auth.models import User from myapp.serializers import UserSerializer - from rest_framework import filters from rest_framework import generics class UserListView(generics.ListAPIView): queryset = User.objects.all() - serializer = UserSerializer - filter_backends = (filters.DjangoFilterBackend,) + serializer_class = UserSerializer + filter_backends = (django_filters.rest_framework.DjangoFilterBackend,) ## Filtering and object lookups @@ -139,12 +139,27 @@ Note that you can use both an overridden `.get_queryset()` and generic filtering ## DjangoFilterBackend -The `DjangoFilterBackend` class supports highly customizable field filtering, using the [django-filter package][django-filter]. +The `django-filter` library includes a `DjangoFilterBackend` class which +supports highly customizable field filtering for REST framework. -To use REST framework's `DjangoFilterBackend`, first install `django-filter`. +To use `DjangoFilterBackend`, first install `django-filter`. pip install django-filter +You should now either add the filter backend to your settings: + + REST_FRAMEWORK = { + 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',) + } + +Or add the filter backend to an individual View or ViewSet. + + from django_filters.rest_framework import DjangoFilterBackend + + class UserListView(generics.ListAPIView): + ... + filter_backends = (DjangoFilterBackend,) + If you are using the browsable API or admin API you may also want to install `django-crispy-forms`, which will enhance the presentation of the filter forms in HTML views, by allowing them to render Bootstrap 3 HTML. pip install django-crispy-forms @@ -174,12 +189,11 @@ For more advanced filtering requirements you can specify a `FilterSet` class tha import django_filters from myapp.models import Product from myapp.serializers import ProductSerializer - from rest_framework import filters from rest_framework import generics - class ProductFilter(filters.FilterSet): - min_price = django_filters.NumberFilter(name="price", lookup_type='gte') - max_price = django_filters.NumberFilter(name="price", lookup_type='lte') + class ProductFilter(django_filters.rest_framework.FilterSet): + min_price = django_filters.NumberFilter(name="price", lookup_expr='gte') + max_price = django_filters.NumberFilter(name="price", lookup_expr='lte') class Meta: model = Product fields = ['category', 'in_stock', 'min_price', 'max_price'] @@ -187,7 +201,7 @@ For more advanced filtering requirements you can specify a `FilterSet` class tha class ProductList(generics.ListAPIView): queryset = Product.objects.all() serializer_class = ProductSerializer - filter_backends = (filters.DjangoFilterBackend,) + filter_backends = (django_filters.rest_framework.DjangoFilterBackend,) filter_class = ProductFilter @@ -199,12 +213,12 @@ You can also span relationships using `django-filter`, let's assume that each product has foreign key to `Manufacturer` model, so we create filter that filters using `Manufacturer` name. For example: + import django_filters from myapp.models import Product from myapp.serializers import ProductSerializer - from rest_framework import filters from rest_framework import generics - class ProductFilter(filters.FilterSet): + class ProductFilter(django_filters.rest_framework.FilterSet): class Meta: model = Product fields = ['category', 'in_stock', 'manufacturer__name'] @@ -218,10 +232,9 @@ This is nice, but it exposes the Django's double underscore convention as part o import django_filters from myapp.models import Product from myapp.serializers import ProductSerializer - from rest_framework import filters from rest_framework import generics - class ProductFilter(filters.FilterSet): + class ProductFilter(django_filters.rest_framework.FilterSet): manufacturer = django_filters.CharFilter(name="manufacturer__name") class Meta: @@ -241,7 +254,6 @@ For more details on using filter sets see the [django-filter documentation][djan * By default filtering is not enabled. If you want to use `DjangoFilterBackend` remember to make sure it is installed by using the `'DEFAULT_FILTER_BACKENDS'` setting. * When using boolean fields, you should use the values `True` and `False` in the URL query parameters, rather than `0`, `1`, `true` or `false`. (The allowed boolean values are currently hardwired in Django's [NullBooleanSelect implementation][nullbooleanselect].) * `django-filter` supports filtering across relationships, using Django's double-underscore syntax. -* For Django 1.3 support, make sure to install `django-filter` version 0.5.4, as later versions drop support for 1.3. --- @@ -257,7 +269,7 @@ The `SearchFilter` class will only be applied if the view has a `search_fields` class UserListView(generics.ListAPIView): queryset = User.objects.all() - serializer = UserSerializer + serializer_class = UserSerializer filter_backends = (filters.SearchFilter,) search_fields = ('username', 'email') @@ -380,7 +392,7 @@ A complete example using both `DjangoObjectPermissionsFilter` and `DjangoObjectP 'change' or 'delete' permissions. """ queryset = Event.objects.all() - serializer = EventSerializer + serializer_class = EventSerializer filter_backends = (filters.DjangoObjectPermissionsFilter,) permission_classes = (myapp.permissions.CustomObjectPermissions,) @@ -417,6 +429,12 @@ Generic filters may also present an interface in the browsable API. To do so you The method should return a rendered HTML string. +## Pagination & schemas + +You can also make the filter controls available to the schema autogeneration +that REST framework provides, by implementing a `get_schema_fields()` method, +which should return a list of `coreapi.Field` instances. + # Third party packages The following third party packages provide additional filter implementations. @@ -433,14 +451,19 @@ The [djangorestframework-word-filter][django-rest-framework-word-search-filter] [django-url-filter][django-url-filter] provides a safe way to filter data via human-friendly URLs. It works very similar to DRF serializers and fields in a sense that they can be nested except they are called filtersets and filters. That provides easy way to filter related data. Also this library is generic-purpose so it can be used to filter other sources of data and not only Django `QuerySet`s. -[cite]: https://docs.djangoproject.com/en/dev/topics/db/queries/#retrieving-specific-objects-with-filters +## drf-url-filters + +[drf-url-filter][drf-url-filter] is a simple Django app to apply filters on drf `ModelViewSet`'s `Queryset` in a clean, simple and configurable way. It also supports validations on incoming query params and their values. A beautiful python package `Voluptuous` is being used for validations on the incoming query parameters. The best part about voluptuous is you can define your own validations as per your query params requirements. + +[cite]: https://docs.djangoproject.com/en/stable/topics/db/queries/#retrieving-specific-objects-with-filters [django-filter]: https://github.com/alex/django-filter -[django-filter-docs]: https://django-filter.readthedocs.org/en/latest/index.html -[guardian]: https://django-guardian.readthedocs.org/ -[view-permissions]: https://django-guardian.readthedocs.org/en/latest/userguide/assign.html +[django-filter-docs]: https://django-filter.readthedocs.io/en/latest/index.html +[guardian]: https://django-guardian.readthedocs.io/ +[view-permissions]: https://django-guardian.readthedocs.io/en/latest/userguide/assign.html [view-permissions-blogpost]: http://blog.nyaruka.com/adding-a-view-permission-to-django-models [nullbooleanselect]: https://github.com/django/django/blob/master/django/forms/widgets.py -[search-django-admin]: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields +[search-django-admin]: https://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields [django-rest-framework-filters]: https://github.com/philipn/django-rest-framework-filters [django-rest-framework-word-search-filter]: https://github.com/trollknurr/django-rest-framework-word-search-filter [django-url-filter]: https://github.com/miki725/django-url-filter +[drf-url-filter]: https://github.com/manjitkumar/drf-url-filters diff --git a/docs/api-guide/format-suffixes.md b/docs/api-guide/format-suffixes.md index 13717b05f..05dde47f2 100644 --- a/docs/api-guide/format-suffixes.md +++ b/docs/api-guide/format-suffixes.md @@ -42,7 +42,7 @@ When using `format_suffix_patterns`, you must make sure to add the `'format'` ke def comment_list(request, format=None): # do stuff... -Or with class based views: +Or with class-based views: class CommentList(APIView): def get(self, request, format=None): diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 4a9581212..606a3787a 100644 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -7,7 +7,7 @@ source: mixins.py > > — [Django Documentation][cite] -One of the key benefits of class based views is the way they allow you to compose bits of reusable behavior. REST framework takes advantage of this by providing a number of pre-built views that provide for commonly used patterns. +One of the key benefits of class-based views is the way they allow you to compose bits of reusable behavior. REST framework takes advantage of this by providing a number of pre-built views that provide for commonly used patterns. The generic views provided by REST framework allow you to quickly build API views that map closely to your database models. @@ -26,7 +26,6 @@ Typically when using the generic views, you'll override the view, and set severa queryset = User.objects.all() serializer_class = UserSerializer permission_classes = (IsAdminUser,) - paginate_by = 100 For more complex cases you might also want to override various methods on the view class. For example. @@ -72,8 +71,6 @@ The following attributes are used to control pagination when used with list view * `pagination_class` - The pagination class that should be used when paginating list results. Defaults to the same value as the `DEFAULT_PAGINATION_CLASS` setting, which is `'rest_framework.pagination.PageNumberPagination'`. -Note that usage of the `paginate_by`, `paginate_by_param` and `page_kwarg` attributes are now pending deprecation. The `pagination_serializer_class` attribute and `DEFAULT_PAGINATION_SERIALIZER_CLASS` setting have been removed completely. Pagination settings should instead be controlled by overriding a pagination class and setting any configuration attributes there. See the pagination documentation for more details. - **Filtering**: * `filter_backends` - A list of filter backend classes that should be used for filtering the queryset. Defaults to the same value as the `DEFAULT_FILTER_BACKENDS` setting. @@ -223,8 +220,6 @@ Also provides a `.partial_update(request, *args, **kwargs)` method, which is sim If an object is updated this returns a `200 OK` response, with a serialized representation of the object as the body of the response. -If an object is created, for example when making a `DELETE` request followed by a `PUT` request to the same URL, this returns a `201 Created` response, with a serialized representation of the object as the body of the response. - If the request data provided for updating the object was invalid, a `400 Bad Request` response will be returned, with the error details as the body of the response. ## DestroyModelMixin @@ -333,7 +328,8 @@ For example, if you need to lookup objects based on multiple fields in the URL c queryset = self.filter_queryset(queryset) # Apply any filter backends filter = {} for field in self.lookup_fields: - filter[field] = self.kwargs[field] + if self.kwargs[field]: # Ignore empty fields. + filter[field] = self.kwargs[field] return get_object_or_404(queryset, **filter) # Lookup the object You can then simply apply this mixin to a view or viewset anytime you need to apply the custom behavior. @@ -386,7 +382,7 @@ The [django-rest-framework-bulk package][django-rest-framework-bulk] implements [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. -[cite]: https://docs.djangoproject.com/en/dev/ref/class-based-views/#base-vs-generic-views +[cite]: https://docs.djangoproject.com/en/stable/ref/class-based-views/#base-vs-generic-views [GenericAPIView]: #genericapiview [ListModelMixin]: #listmodelmixin [CreateModelMixin]: #createmodelmixin diff --git a/docs/api-guide/metadata.md b/docs/api-guide/metadata.md index e12aeb7fd..de28ffd8a 100644 --- a/docs/api-guide/metadata.md +++ b/docs/api-guide/metadata.md @@ -104,6 +104,18 @@ Then configure your settings to use this custom class: 'DEFAULT_METADATA_CLASS': 'myproject.apps.core.MinimalMetadata' } +# Third party packages + +The following third party packages provide additional metadata implementations. + +## DRF-schema-adapter + +[drf-schema-adapter][drf-schema-adapter] is a set of tools that makes it easier to provide schema information to frontend frameworks and libraries. It provides a metadata mixin as well as 2 metadata classes and several adapters suitable to generate [json-schema][json-schema] as well as schema information readable by various libraries. + +You can also write your own adapter to work with your specific frontend. +If you wish to do so, it also provides an exporter that can export those schema information to json files. + [cite]: http://tools.ietf.org/html/rfc7231#section-4.3.7 [no-options]: https://www.mnot.net/blog/2012/10/29/NO_OPTIONS [json-schema]: http://json-schema.org/ +[drf-schema-adapter]: https://github.com/drf-forms/drf-schema-adapter diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index f704a3247..bc7a5602d 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -21,12 +21,15 @@ Pagination can be turned off by setting the pagination class to `None`. ## Setting the pagination style -The default pagination style may be set globally, using the `DEFAULT_PAGINATION_CLASS` settings key. For example, to use the built-in limit/offset pagination, you would do: +The default pagination style may be set globally, using the `DEFAULT_PAGINATION_CLASS` and `PAGE_SIZE` setting keys. For example, to use the built-in limit/offset pagination, you would do something like this: REST_FRAMEWORK = { - 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination' + 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', + 'PAGE_SIZE': 100 } +Note that you need to set both the pagination class, and the page size that should be used. + You can also set the pagination class on an individual view by using the `pagination_class` attribute. Typically you'll want to use the same pagination style throughout your API, although you might want to vary individual aspects of the pagination, such as default or maximum page size, on a per-view basis. ## Modifying the pagination style @@ -47,14 +50,14 @@ You can then apply your new style to a view using the `.pagination_class` attrib class BillingRecordsView(generics.ListAPIView): queryset = Billing.objects.all() - serializer = BillingRecordsSerializer + serializer_class = BillingRecordsSerializer pagination_class = LargeResultsSetPagination Or apply the style globally, using the `DEFAULT_PAGINATION_CLASS` settings key. For example: REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'apps.core.pagination.StandardResultsSetPagination' - } + } --- @@ -109,7 +112,7 @@ To set these attributes you should override the `PageNumberPagination` class, an ## LimitOffsetPagination -This pagination style mirrors the syntax used when looking up multiple database records. The client includes both a "limit" and an +This pagination style mirrors the syntax used when looking up multiple database records. The client includes both a "limit" and an "offset" query parameter. The limit indicates the maximum number of items to return, and is equivalent to the `page_size` in other styles. The offset indicates the starting position of the query in relation to the complete set of unpaginated items. **Request**: @@ -273,6 +276,12 @@ To have your custom pagination class be used by default, use the `DEFAULT_PAGINA API responses for list endpoints will now include a `Link` header, instead of including the pagination links as part of the body of the response, for example: +## Pagination & schemas + +You can also make the pagination controls available to the schema autogeneration +that REST framework provides, by implementing a `get_schema_fields()` method, +which should return a list of `coreapi.Field` instances. + --- ![Link Header][link-header] @@ -312,9 +321,14 @@ The following third party packages are also available. The [`DRF-extensions` package][drf-extensions] includes a [`PaginateByMaxMixin` mixin class][paginate-by-max-mixin] that allows your API clients to specify `?page_size=max` to obtain the maximum allowed page size. -[cite]: https://docs.djangoproject.com/en/dev/topics/pagination/ +## drf-proxy-pagination + +The [`drf-proxy-pagination` package][drf-proxy-pagination] includes a `ProxyPagination` class which allows to choose pagination class with a query parameter. + +[cite]: https://docs.djangoproject.com/en/stable/topics/pagination/ [github-link-pagination]: https://developer.github.com/guides/traversing-with-pagination/ [link-header]: ../img/link-header-pagination.png [drf-extensions]: http://chibisov.github.io/drf-extensions/docs/ [paginate-by-max-mixin]: http://chibisov.github.io/drf-extensions/docs/#paginatebymaxmixin +[drf-proxy-pagination]: https://github.com/tuffnatty/drf-proxy-pagination [disqus-cursor-api]: http://cramer.io/2011/03/08/building-cursors-for-the-disqus-api diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md index 84fd6e5ac..7bf932d06 100644 --- a/docs/api-guide/parsers.md +++ b/docs/api-guide/parsers.md @@ -35,7 +35,7 @@ The default set of parsers may be set globally, using the `DEFAULT_PARSER_CLASSE } You can also set the parsers used for an individual view, or viewset, -using the `APIView` class based views. +using the `APIView` class-based views. from rest_framework.parsers import JSONParser from rest_framework.response import Response @@ -51,7 +51,7 @@ using the `APIView` class based views. return Response({'received data': request.data}) Or, if you're using the `@api_view` decorator with function based views. - + from rest_framework.decorators import api_view from rest_framework.decorators import parser_classes @@ -93,7 +93,9 @@ You will typically want to use both `FormParser` and `MultiPartParser` together Parses raw file upload content. The `request.data` property will be a dictionary with a single key `'file'` containing the uploaded file. -If the view used with `FileUploadParser` is called with a `filename` URL keyword argument, then that argument will be used as the filename. If it is called without a `filename` URL keyword argument, then the client must set the filename in the `Content-Disposition` HTTP header. For example `Content-Disposition: attachment; filename=upload.jpg`. +If the view used with `FileUploadParser` is called with a `filename` URL keyword argument, then that argument will be used as the filename. + +If it is called without a `filename` URL keyword argument, then the client must set the filename in the `Content-Disposition` HTTP header. For example `Content-Disposition: attachment; filename=upload.jpg`. **.media_type**: `*/*` @@ -105,6 +107,7 @@ If the view used with `FileUploadParser` is called with a `filename` URL keyword ##### Basic usage example: + # views.py class FileUploadView(views.APIView): parser_classes = (FileUploadParser,) @@ -115,6 +118,11 @@ If the view used with `FileUploadParser` is called with a `filename` URL keyword # ... return Response(status=204) + # urls.py + urlpatterns = [ + # ... + url(r'^upload/(?P[^/]+)$', FileUploadView.as_view()) + ] --- @@ -216,7 +224,7 @@ Modify your REST framework settings. [jquery-ajax]: http://api.jquery.com/jQuery.ajax/ [cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion -[upload-handlers]: https://docs.djangoproject.com/en/dev/topics/http/file-uploads/#upload-handlers +[upload-handlers]: https://docs.djangoproject.com/en/stable/topics/http/file-uploads/#upload-handlers [rest-framework-yaml]: http://jpadilla.github.io/django-rest-framework-yaml/ [rest-framework-xml]: http://jpadilla.github.io/django-rest-framework-xml/ [yaml]: http://www.yaml.org/ diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index bbac824da..548b14438 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -71,7 +71,7 @@ If not specified, this setting defaults to allowing unrestricted access: ) You can also set the authentication policy on a per-view, or per-viewset basis, -using the `APIView` class based views. +using the `APIView` class-based views. from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response @@ -92,7 +92,7 @@ Or, if you're using the `@api_view` decorator with function based views. from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response - @api_view('GET') + @api_view(['GET']) @permission_classes((IsAuthenticated, )) def example_view(request, format=None): content = { @@ -132,7 +132,7 @@ This permission is suitable if you want to your API to allow read permissions to ## DjangoModelPermissions -This permission class ties into Django's standard `django.contrib.auth` [model permissions][contribauth]. This permission must only be applied to views that has a `.queryset` property set. Authorization will only be granted if the user *is authenticated* and has the *relevant model permissions* assigned. +This permission class ties into Django's standard `django.contrib.auth` [model permissions][contribauth]. This permission must only be applied to views that have a `.queryset` property set. Authorization will only be granted if the user *is authenticated* and has the *relevant model permissions* assigned. * `POST` requests require the user to have the `add` permission on the model. * `PUT` and `PATCH` requests require the user to have the `change` permission on the model. @@ -144,7 +144,7 @@ To use custom model permissions, override `DjangoModelPermissions` and set the ` #### Using with views that do not include a `queryset` attribute. -If you're using this permission with a view that uses an overridden `get_queryset()` method there may not be a `queryset` attribute on the view. In this case we suggest also marking the view with a sential queryset, so that this class can determine the required permissions. For example: +If you're using this permission with a view that uses an overridden `get_queryset()` method there may not be a `queryset` attribute on the view. In this case we suggest also marking the view with a sentinel queryset, so that this class can determine the required permissions. For example: queryset = User.objects.none() # Required for DjangoModelPermissions @@ -164,7 +164,7 @@ As with `DjangoModelPermissions`, this permission must only be applied to views Note that `DjangoObjectPermissions` **does not** require the `django-guardian` package, and should support other object-level backends equally well. -As with `DjangoModelPermissions` you can use custom model permissions by overriding `DjangoModelPermissions` and setting the `.perms_map` property. Refer to the source code for details. +As with `DjangoModelPermissions` you can use custom model permissions by overriding `DjangoObjectPermissions` and setting the `.perms_map` property. Refer to the source code for details. --- @@ -259,14 +259,22 @@ The [REST Condition][rest-condition] package is another extension for building c ## DRY Rest Permissions -The [DRY Rest Permissions][dry-rest-permissions] package provides the ability to define different permissions for individual default and custom actions. This package is made for apps with permissions that are derived from relationships defined in the app's data model. It also supports permission checks being returned to a client app through the API's serializer. Additionally it supports adding permissions to the default and custom list actions to restrict the data they retrive per user. +The [DRY Rest Permissions][dry-rest-permissions] package provides the ability to define different permissions for individual default and custom actions. This package is made for apps with permissions that are derived from relationships defined in the app's data model. It also supports permission checks being returned to a client app through the API's serializer. Additionally it supports adding permissions to the default and custom list actions to restrict the data they retrieve per user. + +## Django Rest Framework Roles + +The [Django Rest Framework Roles][django-rest-framework-roles] package makes it easier to parameterize your API over multiple types of users. + +## Django Rest Framework API Key + +The [Django Rest Framework API Key][django-rest-framework-api-key] package allows you to ensure that every request made to the server requires an API key header. You can generate one from the django admin interface. [cite]: https://developer.apple.com/library/mac/#documentation/security/Conceptual/AuthenticationAndAuthorizationGuide/Authorization/Authorization.html [authentication]: authentication.md [throttling]: throttling.md [filtering]: filtering.md -[contribauth]: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#custom-permissions -[objectpermissions]: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#handling-object-permissions +[contribauth]: https://docs.djangoproject.com/en/stable/topics/auth/customizing/#custom-permissions +[objectpermissions]: https://docs.djangoproject.com/en/stable/topics/auth/customizing/#handling-object-permissions [guardian]: https://github.com/lukaszb/django-guardian [get_objects_for_user]: http://pythonhosted.org/django-guardian/api/guardian.shortcuts.html#get-objects-for-user [2.2-announcement]: ../topics/2.2-announcement.md @@ -275,3 +283,5 @@ The [DRY Rest Permissions][dry-rest-permissions] package provides the ability to [composed-permissions]: https://github.com/niwibe/djangorestframework-composed-permissions [rest-condition]: https://github.com/caxap/rest_condition [dry-rest-permissions]: https://github.com/Helioscene/dry-rest-permissions +[django-rest-framework-roles]: https://github.com/computer-lab/django-rest-framework-roles +[django-rest-framework-api-key]: https://github.com/manosim/django-rest-framework-api-key diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md index 31a78f28f..662fd4809 100644 --- a/docs/api-guide/relations.md +++ b/docs/api-guide/relations.md @@ -39,7 +39,7 @@ In order to explain the various types of relational fields, we'll use a couple o artist = models.CharField(max_length=100) class Track(models.Model): - album = models.ForeignKey(Album, related_name='tracks') + album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE) order = models.IntegerField() title = models.CharField(max_length=100) duration = models.IntegerField() @@ -99,8 +99,8 @@ For example, the following serializer: Would serialize to a representation like this: { - 'album_name': 'The Roots', - 'artist': 'Undun', + 'album_name': 'Undun', + 'artist': 'The Roots', 'tracks': [ 89, 90, @@ -286,7 +286,7 @@ Would serialize to a nested representation like this: ], } -# Writable nested serializers +## Writable nested serializers By default nested serializers are read-only. If you want to support write-operations to a nested serializer field you'll need to create `create()` and/or `update()` methods in order to explicitly specify how the child relationships should be saved. @@ -324,8 +324,14 @@ By default nested serializers are read-only. If you want to support write-operat >>> serializer.save() +--- + # Custom relational fields +In rare cases where none of the existing relational styles fit the representation you need, +you can implement a completely custom relational field, that describes exactly how the +output representation should be generated from the model instance. + To implement a custom relational field, you should override `RelatedField`, and implement the `.to_representation(self, value)` method. This method takes the target of the field as the `value` argument, and should return the representation that should be used to serialize the target. The `value` argument will typically be a model instance. If you want to implement a read-write relational field, you must also implement the `.to_internal_value(self, data)` method. @@ -457,6 +463,8 @@ There are two keyword arguments you can use to control this behavior: - `html_cutoff` - If set this will be the maximum number of choices that will be displayed by a HTML select drop down. Set to `None` to disable any limiting. Defaults to `1000`. - `html_cutoff_text` - If set this will display a textual indicator if the maximum number of items have been cutoff in an HTML select drop down. Defaults to `"More than {count} items…"` +You can also control these globally using the settings `HTML_SELECT_CUTOFF` and `HTML_SELECT_CUTOFF_TEXT`. + In cases where the cutoff is being enforced you may want to instead use a plain input field in the HTML form. You can do so using the `style` keyword argument. For example: assigned_to = serializers.SlugRelatedField( @@ -476,7 +484,7 @@ Note that reverse relationships are not automatically included by the `ModelSeri You'll normally want to ensure that you've set an appropriate `related_name` argument on the relationship, that you can use as the field name. For example: class Track(models.Model): - album = models.ForeignKey(Album, related_name='tracks') + album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE) ... If you have not set a related name for the reverse relationship, you'll need to use the automatically generated related name in the `fields` argument. For example: @@ -489,7 +497,7 @@ See the Django documentation on [reverse relationships][reverse-relationships] f ## Generic relationships -If you want to serialize a generic foreign key, you need to define a custom field, to determine explicitly how you want serialize the targets of the relationship. +If you want to serialize a generic foreign key, you need to define a custom field, to determine explicitly how you want to serialize the targets of the relationship. For example, given the following model for a tag, which has a generic relationship with other arbitrary models: @@ -497,10 +505,10 @@ For example, given the following model for a tag, which has a generic relationsh """ Tags arbitrary model instances using a generic relation. - See: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/ + See: https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/ """ tag_name = models.SlugField() - content_type = models.ForeignKey(ContentType) + content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField() tagged_object = GenericForeignKey('content_type', 'object_id') @@ -585,9 +593,9 @@ The [drf-nested-routers package][drf-nested-routers] provides routers and relati The [rest-framework-generic-relations][drf-nested-relations] library provides read/write serialization for generic foreign keys. [cite]: http://lwn.net/Articles/193245/ -[reverse-relationships]: https://docs.djangoproject.com/en/dev/topics/db/queries/#following-relationships-backward +[reverse-relationships]: https://docs.djangoproject.com/en/stable/topics/db/queries/#following-relationships-backward [routers]: http://www.django-rest-framework.org/api-guide/routers#defaultrouter -[generic-relations]: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/#id1 +[generic-relations]: https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/#id1 [2.2-announcement]: ../topics/2.2-announcement.md [drf-nested-routers]: https://github.com/alanjds/drf-nested-routers [drf-nested-relations]: https://github.com/Ian-Foote/rest-framework-generic-relations diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index b495fd09c..236504850 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -28,7 +28,7 @@ The default set of renderers may be set globally, using the `DEFAULT_RENDERER_CL } You can also set the renderers used for an individual view, or viewset, -using the `APIView` class based views. +using the `APIView` class-based views. from django.contrib.auth.models import User from rest_framework.renderers import JSONRenderer @@ -123,6 +123,8 @@ You can use `TemplateHTMLRenderer` either to return regular HTML pages using RES If you're building websites that use `TemplateHTMLRenderer` along with other renderer classes, you should consider listing `TemplateHTMLRenderer` as the first class in the `renderer_classes` list, so that it will be prioritised first even for browsers that send poorly formed `ACCEPT:` headers. +See the [_HTML & Forms_ Topic Page][html-and-forms] for further examples of `TemplateHTMLRenderer` usage. + **.media_type**: `text/html` **.format**: `'.html'` @@ -471,8 +473,12 @@ Comma-separated values are a plain-text tabular data format, that can be easily [Django REST Pandas] provides a serializer and renderers that support additional data processing and output via the [Pandas] DataFrame API. Django REST Pandas includes renderers for Pandas-style CSV files, Excel workbooks (both `.xls` and `.xlsx`), and a number of [other formats]. It is maintained by [S. Andrew Sheppard][sheppard] as part of the [wq Project][wq]. +## LaTeX -[cite]: https://docs.djangoproject.com/en/dev/ref/template-response/#the-rendering-process +[Rest Framework Latex] provides a renderer that outputs PDFs using Laulatex. It is maintained by [Pebble (S/F Software)][mypebble]. + + +[cite]: https://docs.djangoproject.com/en/stable/stable/template-response/#the-rendering-process [conneg]: content-negotiation.md [html-and-forms]: ../topics/html-and-forms.md [browser-accept-headers]: http://www.gethifi.com/blog/browser-rest-http-accept-headers @@ -481,7 +487,7 @@ Comma-separated values are a plain-text tabular data format, that can be easily [quote]: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven [application/vnd.github+json]: http://developer.github.com/v3/media/ [application/vnd.collection+json]: http://www.amundsen.com/media-types/collection/ -[django-error-views]: https://docs.djangoproject.com/en/dev/topics/http/views/#customizing-error-views +[django-error-views]: https://docs.djangoproject.com/en/stable/topics/http/views/#customizing-error-views [rest-framework-jsonp]: http://jpadilla.github.io/django-rest-framework-jsonp/ [cors]: http://www.w3.org/TR/cors/ [cors-docs]: http://www.django-rest-framework.org/topics/ajax-csrf-cors/ @@ -506,3 +512,5 @@ Comma-separated values are a plain-text tabular data format, that can be easily [other formats]: https://github.com/wq/django-rest-pandas#supported-formats [sheppard]: https://github.com/sheppard [wq]: https://github.com/wq +[mypebble]: https://github.com/mypebble +[Rest Framework Latex]: https://github.com/mypebble/rest-framework-latex diff --git a/docs/api-guide/requests.md b/docs/api-guide/requests.md index 94cdb14f8..393b1ab9a 100644 --- a/docs/api-guide/requests.md +++ b/docs/api-guide/requests.md @@ -118,10 +118,6 @@ For more information see the [browser enhancements documentation]. You won't typically need to directly access the request's content, as you'll normally rely on REST framework's default request parsing behavior. -If you do need to access the raw content directly, you should use the `.stream` property in preference to using `request.content`, as it provides transparent support for browser-based non-form content. - -For more information see the [browser enhancements documentation]. - --- # Standard HttpRequest attributes diff --git a/docs/api-guide/responses.md b/docs/api-guide/responses.md index 97f312710..8ee14eefa 100644 --- a/docs/api-guide/responses.md +++ b/docs/api-guide/responses.md @@ -91,5 +91,5 @@ As with any other `TemplateResponse`, this method is called to render the serial You won't typically need to call `.render()` yourself, as it's handled by Django's standard response cycle. -[cite]: https://docs.djangoproject.com/en/dev/ref/template-response/ +[cite]: https://docs.djangoproject.com/en/stable/stable/template-response/ [statuscodes]: status-codes.md diff --git a/docs/api-guide/reverse.md b/docs/api-guide/reverse.md index 71fb83f9e..ee0b2054f 100644 --- a/docs/api-guide/reverse.md +++ b/docs/api-guide/reverse.md @@ -23,7 +23,7 @@ There's no requirement for you to use them, but if you do then the self-describi **Signature:** `reverse(viewname, *args, **kwargs)` -Has the same behavior as [`django.core.urlresolvers.reverse`][reverse], except that it returns a fully qualified URL, using the request to determine the host and port. +Has the same behavior as [`django.urls.reverse`][reverse], except that it returns a fully qualified URL, using the request to determine the host and port. You should **include the request as a keyword argument** to the function, for example: @@ -44,12 +44,12 @@ You should **include the request as a keyword argument** to the function, for ex **Signature:** `reverse_lazy(viewname, *args, **kwargs)` -Has the same behavior as [`django.core.urlresolvers.reverse_lazy`][reverse-lazy], except that it returns a fully qualified URL, using the request to determine the host and port. +Has the same behavior as [`django.urls.reverse_lazy`][reverse-lazy], except that it returns a fully qualified URL, using the request to determine the host and port. As with the `reverse` function, you should **include the request as a keyword argument** to the function, for example: api_root = reverse_lazy('api-root', request=request) [cite]: http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_5 -[reverse]: https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse -[reverse-lazy]: https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse-lazy +[reverse]: https://docs.djangoproject.com/en/stable/topics/http/urls/#reverse +[reverse-lazy]: https://docs.djangoproject.com/en/stable/topics/http/urls/#reverse-lazy diff --git a/docs/api-guide/routers.md b/docs/api-guide/routers.md index d73606da2..d3f4186ad 100644 --- a/docs/api-guide/routers.md +++ b/docs/api-guide/routers.md @@ -118,6 +118,26 @@ The above example would now generate the following URL pattern: * URL pattern: `^users/{pk}/change-password/$` Name: `'user-change-password'` +In the case you do not want to use the default name generated for your custom action, you can use the url_name parameter to customize it. + +For example, if you want to change the name of our custom action to `'user-change-password'`, you could write: + + from myapp.permissions import IsAdminOrIsSelf + from rest_framework.decorators import detail_route + + class UserViewSet(ModelViewSet): + ... + + @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf], url_name='change-password') + def set_password(self, request, pk=None): + ... + +The above example would now generate the following URL pattern: + +* URL pattern: `^users/{pk}/set_password/$` Name: `'user-change-password'` + +You can also use url_path and url_name parameters together to obtain extra control on URL generation for custom views. + For more information see the viewset documentation on [marking extra actions for routing][route-decorators]. # API Guide @@ -226,9 +246,9 @@ The following example will only route to the `list` and `retrieve` actions, and ), Route( url=r'^{prefix}/{lookup}$', - mapping={'get': 'retrieve'}, - name='{basename}-detail', - initkwargs={'suffix': 'Detail'} + mapping={'get': 'retrieve'}, + name='{basename}-detail', + initkwargs={'suffix': 'Detail'} ), DynamicDetailRoute( url=r'^{prefix}/{lookup}/{methodnamehyphen}$', diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md new file mode 100644 index 000000000..f43ff56bd --- /dev/null +++ b/docs/api-guide/schemas.md @@ -0,0 +1,557 @@ +source: schemas.py + +# Schemas + +> A machine-readable [schema] describes what resources are available via the API, what their URLs are, how they are represented and what operations they support. +> +> — Heroku, [JSON Schema for the Heroku Platform API][cite] + +API schemas are a useful tool that allow for a range of use cases, including +generating reference documentation, or driving dynamic client libraries that +can interact with your API. + +## Representing schemas internally + +REST framework uses [Core API][coreapi] in order to model schema information in +a format-independent representation. This information can then be rendered +into various different schema formats, or used to generate API documentation. + +When using Core API, a schema is represented as a `Document` which is the +top-level container object for information about the API. Available API +interactions are represented using `Link` objects. Each link includes a URL, +HTTP method, and may include a list of `Field` instances, which describe any +parameters that may be accepted by the API endpoint. The `Link` and `Field` +instances may also include descriptions, that allow an API schema to be +rendered into user documentation. + +Here's an example of an API description that includes a single `search` +endpoint: + + coreapi.Document( + title='Flight Search API', + url='https://api.example.org/', + content={ + 'search': coreapi.Link( + url='/search/', + action='get', + fields=[ + coreapi.Field( + name='from', + required=True, + location='query', + description='City name or airport code.' + ), + coreapi.Field( + name='to', + required=True, + location='query', + description='City name or airport code.' + ), + coreapi.Field( + name='date', + required=True, + location='query', + description='Flight date in "YYYY-MM-DD" format.' + ) + ], + description='Return flight availability and prices.' + ) + } + ) + +## Schema output formats + +In order to be presented in an HTTP response, the internal representation +has to be rendered into the actual bytes that are used in the response. + +[Core JSON][corejson] is designed as a canonical format for use with Core API. +REST framework includes a renderer class for handling this media type, which +is available as `renderers.CoreJSONRenderer`. + +Other schema formats such as [Open API][open-api] ("Swagger"), +[JSON HyperSchema][json-hyperschema], or [API Blueprint][api-blueprint] can +also be supported by implementing a custom renderer class. + +## Schemas vs Hypermedia + +It's worth pointing out here that Core API can also be used to model hypermedia +responses, which present an alternative interaction style to API schemas. + +With an API schema, the entire available interface is presented up-front +as a single endpoint. Responses to individual API endpoints are then typically +presented as plain data, without any further interactions contained in each +response. + +With Hypermedia, the client is instead presented with a document containing +both data and available interactions. Each interaction results in a new +document, detailing both the current state and the available interactions. + +Further information and support on building Hypermedia APIs with REST framework +is planned for a future version. + +--- + +# Adding a schema + +You'll need to install the `coreapi` package in order to add schema support +for REST framework. + + pip install coreapi + +REST framework includes functionality for auto-generating a schema, +or allows you to specify one explicitly. There are a few different ways to +add a schema to your API, depending on exactly what you need. + +## The get_schema_view shortcut + +The simplest way to include a schema in your project is to use the +`get_schema_view()` function. + + schema_view = get_schema_view(title="Server Monitoring API") + + urlpatterns = [ + url('^$', schema_view), + ... + ] + +Once the view has been added, you'll be able to make API requests to retrieve +the auto-generated schema definition. + + $ http http://127.0.0.1:8000/ Accept:application/vnd.coreapi+json + HTTP/1.0 200 OK + Allow: GET, HEAD, OPTIONS + Content-Type: application/vnd.coreapi+json + + { + "_meta": { + "title": "Server Monitoring API" + }, + "_type": "document", + ... + } + +The arguments to `get_schema_view()` are: + +#### `title` + +May be used to provide a descriptive title for the schema definition. + +#### `url` + +May be used to pass a canonical URL for the schema. + + schema_view = get_schema_view( + title='Server Monitoring API', + url='https://www.example.org/api/' + ) + +#### `urlconf` + +A string representing the import path to the URL conf that you want +to generate an API schema for. This defaults to the value of Django's +ROOT_URLCONF setting. + + schema_view = get_schema_view( + title='Server Monitoring API', + url='https://www.example.org/api/', + urlconf='myproject.urls' + ) + +#### `renderer_classes` + +May be used to pass the set of renderer classes that can be used to render the API root endpoint. + + from rest_framework.renderers import CoreJSONRenderer + from my_custom_package import APIBlueprintRenderer + + schema_view = get_schema_view( + title='Server Monitoring API', + url='https://www.example.org/api/', + renderer_classes=[CoreJSONRenderer, APIBlueprintRenderer] + ) + +## Using an explicit schema view + +If you need a little more control than the `get_schema_view()` shortcut gives you, +then you can use the `SchemaGenerator` class directly to auto-generate the +`Document` instance, and to return that from a view. + +This option gives you the flexibility of setting up the schema endpoint +with whatever behaviour you want. For example, you can apply different +permission, throttling, or authentication policies to the schema endpoint. + +Here's an example of using `SchemaGenerator` together with a view to +return the schema. + +**views.py:** + + from rest_framework.decorators import api_view, renderer_classes + from rest_framework import renderers, response, schemas + + generator = schemas.SchemaGenerator(title='Bookings API') + + @api_view() + @renderer_classes([renderers.CoreJSONRenderer]) + def schema_view(request): + schema = generator.get_schema(request) + return response.Response(schema) + +**urls.py:** + + urlpatterns = [ + url('/', schema_view), + ... + ] + +You can also serve different schemas to different users, depending on the +permissions they have available. This approach can be used to ensure that +unauthenticated requests are presented with a different schema to +authenticated requests, or to ensure that different parts of the API are +made visible to different users depending on their role. + +In order to present a schema with endpoints filtered by user permissions, +you need to pass the `request` argument to the `get_schema()` method, like so: + + @api_view() + @renderer_classes([renderers.CoreJSONRenderer]) + def schema_view(request): + generator = schemas.SchemaGenerator(title='Bookings API') + return response.Response(generator.get_schema(request=request)) + +## Explicit schema definition + +An alternative to the auto-generated approach is to specify the API schema +explicitly, by declaring a `Document` object in your codebase. Doing so is a +little more work, but ensures that you have full control over the schema +representation. + + import coreapi + from rest_framework.decorators import api_view, renderer_classes + from rest_framework import renderers, response + + schema = coreapi.Document( + title='Bookings API', + content={ + ... + } + ) + + @api_view() + @renderer_classes([renderers.CoreJSONRenderer]) + def schema_view(request): + return response.Response(schema) + +## Static schema file + +A final option is to write your API schema as a static file, using one +of the available formats, such as Core JSON or Open API. + +You could then either: + +* Write a schema definition as a static file, and [serve the static file directly][static-files]. +* Write a schema definition that is loaded using `Core API`, and then + rendered to one of many available formats, depending on the client request. + +--- + +# Schemas as documentation + +One common usage of API schemas is to use them to build documentation pages. + +The schema generation in REST framework uses docstrings to automatically +populate descriptions in the schema document. + +These descriptions will be based on: + +* The corresponding method docstring if one exists. +* A named section within the class docstring, which can be either single line or multi-line. +* The class docstring. + +## Examples + +An `APIView`, with an explicit method docstring. + + class ListUsernames(APIView): + def get(self, request): + """ + Return a list of all user names in the system. + """ + usernames = [user.username for user in User.objects.all()] + return Response(usernames) + +A `ViewSet`, with an explict action docstring. + + class ListUsernames(ViewSet): + def list(self, request): + """ + Return a list of all user names in the system. + """ + usernames = [user.username for user in User.objects.all()] + return Response(usernames) + +A generic view with sections in the class docstring, using single-line style. + + class UserList(generics.ListCreateAPIView): + """ + get: List all the users. + post: Create a new user. + """ + queryset = User.objects.all() + serializer_class = UserSerializer + permission_classes = (IsAdminUser,) + +A generic viewset with sections in the class docstring, using multi-line style. + + class UserViewSet(viewsets.ModelViewSet): + """ + API endpoint that allows users to be viewed or edited. + + retrieve: + Return a user instance. + + list: + Return all users, ordered by most recently joined. + """ + queryset = User.objects.all().order_by('-date_joined') + serializer_class = UserSerializer + +--- + +# Alternate schema formats + +In order to support an alternate schema format, you need to implement a custom renderer +class that handles converting a `Document` instance into a bytestring representation. + +If there is a Core API codec package that supports encoding into the format you +want to use then implementing the renderer class can be done by using the codec. + +## Example + +For example, the `openapi_codec` package provides support for encoding or decoding +to the Open API ("Swagger") format: + + from rest_framework import renderers + from openapi_codec import OpenAPICodec + + class SwaggerRenderer(renderers.BaseRenderer): + media_type = 'application/openapi+json' + format = 'swagger' + + def render(self, data, media_type=None, renderer_context=None): + codec = OpenAPICodec() + return codec.dump(data) + +--- + +# API Reference + +## SchemaGenerator + +A class that deals with introspecting your API views, which can be used to +generate a schema. + +Typically you'll instantiate `SchemaGenerator` with a single argument, like so: + + generator = SchemaGenerator(title='Stock Prices API') + +Arguments: + +* `title` **required** - The name of the API. +* `url` - The root URL of the API schema. This option is not required unless the schema is included under path prefix. +* `patterns` - A list of URLs to inspect when generating the schema. Defaults to the project's URL conf. +* `urlconf` - A URL conf module name to use when generating the schema. Defaults to `settings.ROOT_URLCONF`. + +### get_schema(self, request) + +Returns a `coreapi.Document` instance that represents the API schema. + + @api_view + @renderer_classes([renderers.CoreJSONRenderer]) + def schema_view(request): + generator = schemas.SchemaGenerator(title='Bookings API') + return Response(generator.get_schema()) + +The `request` argument is optional, and may be used if you want to apply per-user +permissions to the resulting schema generation. + +### get_links(self, request) + +Return a nested dictionary containing all the links that should be included in the API schema. + +This is a good point to override if you want to modify the resulting structure of the generated schema, +as you can build a new dictionary with a different layout. + +### get_link(self, path, method, view) + +Returns a `coreapi.Link` instance corresponding to the given view. + +You can override this if you need to provide custom behaviors for particular views. + +### get_description(self, path, method, view) + +Returns a string to use as the link description. By default this is based on the +view docstring as described in the "Schemas as Documentation" section above. + +### get_encoding(self, path, method, view) + +Returns a string to indicate the encoding for any request body, when interacting +with the given view. Eg. `'application/json'`. May return a blank string for views +that do not expect a request body. + +### get_path_fields(self, path, method, view): + +Return a list of `coreapi.Link()` instances. One for each path parameter in the URL. + +### get_serializer_fields(self, path, method, view) + +Return a list of `coreapi.Link()` instances. One for each field in the serializer class used by the view. + +### get_pagination_fields(self, path, method, view + +Return a list of `coreapi.Link()` instances, as returned by the `get_schema_fields()` method on any pagination class used by the view. + +### get_filter_fields(self, path, method, view) + +Return a list of `coreapi.Link()` instances, as returned by the `get_schema_fields()` method of any filter classes used by the view. + +--- + +## Core API + +This documentation gives a brief overview of the components within the `coreapi` +package that are used to represent an API schema. + +Note that these classes are imported from the `coreapi` package, rather than +from the `rest_framework` package. + +### Document + +Represents a container for the API schema. + +#### `title` + +A name for the API. + +#### `url` + +A canonical URL for the API. + +#### `content` + +A dictionary, containing the `Link` objects that the schema contains. + +In order to provide more structure to the schema, the `content` dictionary +may be nested, typically to a second level. For example: + + content={ + "bookings": { + "list": Link(...), + "create": Link(...), + ... + }, + "venues": { + "list": Link(...), + ... + }, + ... + } + +### Link + +Represents an individual API endpoint. + +#### `url` + +The URL of the endpoint. May be a URI template, such as `/users/{username}/`. + +#### `action` + +The HTTP method associated with the endpoint. Note that URLs that support +more than one HTTP method, should correspond to a single `Link` for each. + +#### `fields` + +A list of `Field` instances, describing the available parameters on the input. + +#### `description` + +A short description of the meaning and intended usage of the endpoint. + +### Field + +Represents a single input parameter on a given API endpoint. + +#### `name` + +A descriptive name for the input. + +#### `required` + +A boolean, indicated if the client is required to included a value, or if +the parameter can be omitted. + +#### `location` + +Determines how the information is encoded into the request. Should be one of +the following strings: + +**"path"** + +Included in a templated URI. For example a `url` value of `/products/{product_code}/` could be used together with a `"path"` field, to handle API inputs in a URL path such as `/products/slim-fit-jeans/`. + +These fields will normally correspond with [named arguments in the project URL conf][named-arguments]. + +**"query"** + +Included as a URL query parameter. For example `?search=sale`. Typically for `GET` requests. + +These fields will normally correspond with pagination and filtering controls on a view. + +**"form"** + +Included in the request body, as a single item of a JSON object or HTML form. For example `{"colour": "blue", ...}`. Typically for `POST`, `PUT` and `PATCH` requests. Multiple `"form"` fields may be included on a single link. + +These fields will normally correspond with serializer fields on a view. + +**"body"** + +Included as the complete request body. Typically for `POST`, `PUT` and `PATCH` requests. No more than one `"body"` field may exist on a link. May not be used together with `"form"` fields. + +These fields will normally correspond with views that use `ListSerializer` to validate the request input, or with file upload views. + +#### `encoding` + +**"application/json"** + +JSON encoded request content. Corresponds to views using `JSONParser`. +Valid only if either one or more `location="form"` fields, or a single +`location="body"` field is included on the `Link`. + +**"multipart/form-data"** + +Multipart encoded request content. Corresponds to views using `MultiPartParser`. +Valid only if one or more `location="form"` fields is included on the `Link`. + +**"application/x-www-form-urlencoded"** + +URL encoded request content. Corresponds to views using `FormParser`. Valid +only if one or more `location="form"` fields is included on the `Link`. + +**"application/octet-stream"** + +Binary upload request content. Corresponds to views using `FileUploadParser`. +Valid only if a `location="body"` field is included on the `Link`. + +#### `description` + +A short description of the meaning and intended usage of the input field. + + +[cite]: https://blog.heroku.com/archives/2014/1/8/json_schema_for_heroku_platform_api +[coreapi]: http://www.coreapi.org/ +[corejson]: http://www.coreapi.org/specification/encoding/#core-json-encoding +[open-api]: https://openapis.org/ +[json-hyperschema]: http://json-schema.org/latest/json-schema-hypermedia.html +[api-blueprint]: https://apiblueprint.org/ +[static-files]: https://docs.djangoproject.com/en/stable/howto/static-files/ +[named-arguments]: https://docs.djangoproject.com/en/stable/topics/http/urls/#named-groups diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 6a5519930..6602d5e2c 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -442,7 +442,7 @@ Declaring a `ModelSerializer` looks like this: By default, all the model fields on the class will be mapped to a corresponding serializer fields. -Any relationships such as foreign keys on the model will be mapped to `PrimaryKeyRelatedField`. Reverse relationships are not included by default unless explicitly included as described below. +Any relationships such as foreign keys on the model will be mapped to `PrimaryKeyRelatedField`. Reverse relationships are not included by default unless explicitly included as specified in the [serializer relations][relations] documentation. #### Inspecting a `ModelSerializer` @@ -452,7 +452,7 @@ To do so, open the Django shell, using `python manage.py shell`, then import the >>> from myapp.serializers import AccountSerializer >>> serializer = AccountSerializer() - >>> print repr(serializer) # Or `print(repr(serializer))` in Python 3.x. + >>> print(repr(serializer)) AccountSerializer(): id = IntegerField(label='ID', read_only=True) name = CharField(allow_blank=True, max_length=100, required=False) @@ -578,16 +578,6 @@ Alternative representations include serializing using hyperlinks, serializing co For full details see the [serializer relations][relations] documentation. -## Inheritance of the 'Meta' class - -The inner `Meta` class on serializers is not inherited from parent classes by default. This is the same behavior as with Django's `Model` and `ModelForm` classes. If you want the `Meta` class to inherit from a parent class you must do so explicitly. For example: - - class AccountSerializer(MyBaseSerializer): - class Meta(MyBaseSerializer.Meta): - model = Account - -Typically we would recommend *not* using inheritance on inner Meta classes, but instead declaring all options explicitly. - ## Customizing field mappings The ModelSerializer class also exposes an API that you can override in order to alter how serializer fields are automatically determined when instantiating the serializer. @@ -678,6 +668,25 @@ You can explicitly include the primary key by adding it to the `fields` option, model = Account fields = ('url', 'id', 'account_name', 'users', 'created') +## Absolute and relative URLs + +When instantiating a `HyperlinkedModelSerializer` you must include the current +`request` in the serializer context, for example: + + serializer = AccountSerializer(queryset, context={'request': request}) + +Doing so will ensure that the hyperlinks can include an appropriate hostname, +so that the resulting representation uses fully qualified URLs, such as: + + http://api.example.com/accounts/1/ + +Rather than relative URLs, such as: + + /accounts/1/ + +If you *do* want to use relative URLs, you should explicitly pass `{'request': None}` +in the serializer context. + ## How hyperlinked views are determined There needs to be a way of determining which views should be used for hyperlinking to model instances. @@ -731,9 +740,17 @@ The `ListSerializer` class provides the behavior for serializing and validating When a serializer is instantiated and `many=True` is passed, a `ListSerializer` instance will be created. The serializer class then becomes a child of the parent `ListSerializer` +The following argument can also be passed to a `ListSerializer` field or a serializer that is passed `many=True`: + +### `allow_empty` + +This is `True` by default, but can be set to `False` if you want to disallow empty lists as valid input. + +### Customizing `ListSerializer` behavior + There *are* a few use cases when you might want to customize the `ListSerializer` behavior. For example: -* You want to provide particular validation of the lists, such as always ensuring that there is at least one element in a list. +* You want to provide particular validation of the lists, such as checking that one element does not conflict with another element in a list. * You want to customize the create or update behavior of multiple objects. For these cases you can modify the class that is used when `many=True` is passed, by using the `list_serializer_class` option on the serializer `Meta` class. @@ -849,7 +866,7 @@ There are four methods that can be overridden, depending on what functionality y * `.to_internal_value()` - Override this to support deserialization, for write operations. * `.create()` and `.update()` - Override either or both of these to support saving instances. -Because this class provides the same interface as the `Serializer` class, you can use it with the existing generic class based views exactly as you would for a regular `Serializer` or `ModelSerializer`. +Because this class provides the same interface as the `Serializer` class, you can use it with the existing generic class-based views exactly as you would for a regular `Serializer` or `ModelSerializer`. The only difference you'll notice when doing so is the `BaseSerializer` classes will not generate HTML forms in the browsable API. This is because the data they return does not include all the field information that would allow each field to be rendered into a suitable HTML input. @@ -998,6 +1015,40 @@ If any of the validation fails, then the method should raise a `serializers.Vali The `data` argument passed to this method will normally be the value of `request.data`, so the datatype it provides will depend on the parser classes you have configured for your API. +## Serializer Inheritance + +Similar to Django forms, you can extend and reuse serializers through inheritance. This allows you to declare a common set of fields or methods on a parent class that can then be used in a number of serializers. For example, + + class MyBaseSerializer(Serializer): + my_field = serializers.CharField() + + def validate_my_field(self): + ... + + class MySerializer(MyBaseSerializer): + ... + +Like Django's `Model` and `ModelForm` classes, the inner `Meta` class on serializers does not implicitly inherit from it's parents' inner `Meta` classes. If you want the `Meta` class to inherit from a parent class you must do so explicitly. For example: + + class AccountSerializer(MyBaseSerializer): + class Meta(MyBaseSerializer.Meta): + model = Account + +Typically we would recommend *not* using inheritance on inner Meta classes, but instead declaring all options explicitly. + +Additionally, the following caveats apply to serializer inheritance: + +* Normal Python name resolution rules apply. If you have multiple base classes that declare a `Meta` inner class, only the first one will be used. This means the child’s `Meta`, if it exists, otherwise the `Meta` of the first parent, etc. +* It’s possible to declaratively remove a `Field` inherited from a parent class by setting the name to be `None` on the subclass. + + class MyBaseSerializer(ModelSerializer): + my_field = serializers.CharField() + + class MySerializer(MyBaseSerializer): + my_field = None + + However, you can only use this technique to opt out from a field defined declaratively by a parent class; it won’t prevent the `ModelSerializer` from generating a default field. To opt-out from default fields, see [Specifying which fields to include](#specifying-which-fields-to-include). + ## Dynamically modifying fields Once a serializer has been initialized, the dictionary of fields that are set on the serializer may be accessed using the `.fields` attribute. Accessing and modifying this attribute allows you to dynamically modify the serializer. @@ -1062,6 +1113,7 @@ The following third party packages are also available. The [django-rest-marshmallow][django-rest-marshmallow] package provides an alternative implementation for serializers, using the python [marshmallow][marshmallow] library. It exposes the same API as the REST framework serializers, and can be used as a drop-in replacement in some use-cases. ## Serpy + The [serpy][serpy] package is an alternative implementation for serializers that is built for speed. [Serpy][serpy] serializes complex datatypes to simple native types. The native types can be easily converted to JSON or any other format needed. ## MongoengineModelSerializer @@ -1080,15 +1132,50 @@ The [django-rest-framework-hstore][django-rest-framework-hstore] package provide The [dynamic-rest][dynamic-rest] package extends the ModelSerializer and ModelViewSet interfaces, adding API query parameters for filtering, sorting, and including / excluding all fields and relationships defined by your serializers. +## Dynamic Fields Mixin + +The [drf-dynamic-fields][drf-dynamic-fields] package provides a mixin to dynamically limit the fields per serializer to a subset specified by an URL parameter. + +## DRF FlexFields + +The [drf-flex-fields][drf-flex-fields] package extends the ModelSerializer and ModelViewSet to provide commonly used functionality for dynamically setting fields and expanding primitive fields to nested models, both from URL parameters and your serializer class definitions. + +## Serializer Extensions + +The [django-rest-framework-serializer-extensions][drf-serializer-extensions] +package provides a collection of tools to DRY up your serializers, by allowing +fields to be defined on a per-view/request basis. Fields can be whitelisted, +blacklisted and child serializers can be optionally expanded. + +## HTML JSON Forms + +The [html-json-forms][html-json-forms] package provides an algorithm and serializer for processing `
` submissions per the (inactive) [HTML JSON Form specification][json-form-spec]. The serializer facilitates processing of arbitrarily nested JSON structures within HTML. For example, `` will be interpreted as `{"items": [{"id": "5"}]}`. + +## DRF-Base64 + +[DRF-Base64][drf-base64] provides a set of field and model serializers that handles the upload of base64-encoded files. + +## QueryFields + +[djangorestframework-queryfields][djangorestframework-queryfields] allows API clients to specify which fields will be sent in the response via inclusion/exclusion query parameters. + + [cite]: https://groups.google.com/d/topic/django-users/sVFaOfQi4wY/discussion [relations]: relations.md -[model-managers]: https://docs.djangoproject.com/en/dev/topics/db/managers/ +[model-managers]: https://docs.djangoproject.com/en/stable/topics/db/managers/ [encapsulation-blogpost]: http://www.dabapps.com/blog/django-models-and-encapsulation/ [django-rest-marshmallow]: http://tomchristie.github.io/django-rest-marshmallow/ -[marshmallow]: https://marshmallow.readthedocs.org/en/latest/ +[marshmallow]: https://marshmallow.readthedocs.io/en/latest/ [serpy]: https://github.com/clarkduvall/serpy [mongoengine]: https://github.com/umutbozkurt/django-rest-framework-mongoengine [django-rest-framework-gis]: https://github.com/djangonauts/django-rest-framework-gis [django-rest-framework-hstore]: https://github.com/djangonauts/django-rest-framework-hstore [django-hstore]: https://github.com/djangonauts/django-hstore [dynamic-rest]: https://github.com/AltSchool/dynamic-rest +[html-json-forms]: https://github.com/wq/html-json-forms +[drf-flex-fields]: https://github.com/rsinger86/drf-flex-fields +[json-form-spec]: https://www.w3.org/TR/html-json-forms/ +[drf-dynamic-fields]: https://github.com/dbrgn/drf-dynamic-fields +[drf-base64]: https://bitbucket.org/levit_scs/drf_base64 +[drf-serializer-extensions]: https://github.com/evenicoulddoit/django-rest-framework-serializer-extensions +[djangorestframework-queryfields]: http://djangorestframework-queryfields.readthedocs.io/ diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 23691dec1..aaedd463e 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -36,7 +36,7 @@ The `api_settings` object will check for any user-defined settings, and otherwis ## API policy settings -*The following settings control the basic API policies, and are applied to every `APIView` class based view, or `@api_view` function based view.* +*The following settings control the basic API policies, and are applied to every `APIView` class-based view, or `@api_view` function based view.* #### DEFAULT_RENDERER_CLASSES @@ -98,13 +98,19 @@ Default: `'rest_framework.negotiation.DefaultContentNegotiation'` ## Generic view settings -*The following settings control the behavior of the generic class based views.* +*The following settings control the behavior of the generic class-based views.* #### DEFAULT_PAGINATION_SERIALIZER_CLASS -A class the determines the default serialization style for paginated responses. +--- -Default: `rest_framework.pagination.PaginationSerializer` +**This setting has been removed.** + +The pagination API does not use serializers to determine the output format, and +you'll need to instead override the `get_paginated_response method on a +pagination class in order to specify how the output format is controlled. + +--- #### DEFAULT_FILTER_BACKENDS @@ -113,6 +119,16 @@ If set to `None` then generic filtering is disabled. #### PAGINATE_BY +--- + +**This setting has been removed.** + +See the pagination documentation for further guidance on [setting the pagination style](pagination.md#modifying-the-pagination-style). + +--- + +#### PAGE_SIZE + The default page size to use for pagination. If set to `None`, pagination is disabled by default. Default: `None` @@ -121,27 +137,12 @@ Default: `None` --- -**This setting is pending deprecation.** +**This setting has been removed.** See the pagination documentation for further guidance on [setting the pagination style](pagination.md#modifying-the-pagination-style). --- -The name of a query parameter, which can be used by the client to override the default page size to use for pagination. If set to `None`, clients may not override the default page size. - -For example, given the following settings: - - REST_FRAMEWORK = { - 'PAGINATE_BY': 10, - 'PAGINATE_BY_PARAM': 'page_size', - } - -A client would be able to modify the pagination size by using the `page_size` query parameter. For example: - - GET http://example.com/api/accounts?page_size=25 - -Default: `None` - #### MAX_PAGINATE_BY --- @@ -152,22 +153,6 @@ See the pagination documentation for further guidance on [setting the pagination --- -The maximum page size to allow when the page size is specified by the client. If set to `None`, then no maximum limit is applied. - -For example, given the following settings: - - REST_FRAMEWORK = { - 'PAGINATE_BY': 10, - 'PAGINATE_BY_PARAM': 'page_size', - 'MAX_PAGINATE_BY': 100 - } - -A client request like the following would return a paginated list of up to 100 items. - - GET http://example.com/api/accounts?page_size=999 - -Default: `None` - ### SEARCH_PARAM The name of a query parameter, which can be used to specify the search term used by `SearchFilter`. @@ -196,7 +181,7 @@ If set, this value will restrict the set of versions that may be returned by the Default: `None` -#### VERSION_PARAMETER +#### VERSION_PARAM The string that should used for any versioning parameters, such as in the media type or URL query parameters. @@ -249,6 +234,28 @@ Default: --- +## Schema generation controls + +#### SCHEMA_COERCE_PATH_PK + +If set, this maps the `'pk'` identifier in the URL conf onto the actual field +name when generating a schema path parameter. Typically this will be `'id'`. +This gives a more suitable representation as "primary key" is an implementation +detail, whereas "identifier" is a more general concept. + +Default: `True` + +#### SCHEMA_COERCE_METHOD_NAMES + +If set, this is used to map internal viewset method names onto external action +names used in the schema generation. This allows us to generate names that +are more suitable for an external representation than those that are used +internally in the codebase. + +Default: `{'retrieve': 'read', 'destroy': 'delete'}` + +--- + ## Content type controls #### URL_FORMAT_OVERRIDE @@ -397,6 +404,22 @@ This should be a function with the following signature: Default: `'rest_framework.views.get_view_description'` +## HTML Select Field cutoffs + +Global settings for [select field cutoffs for rendering relational fields](relations.md#select-field-cutoffs) in the browsable API. + +#### HTML_SELECT_CUTOFF + +Global setting for the `html_cutoff` value. Must be an integer. + +Default: 1000 + +#### HTML_SELECT_CUTOFF_TEXT + +A string representing a global setting for `html_cutoff_text`. + +Default: `"More than {count} items..."` + --- ## Miscellaneous settings @@ -433,7 +456,7 @@ An integer of 0 or more, that may be used to specify the number of application p Default: `None` -[cite]: http://www.python.org/dev/peps/pep-0020/ +[cite]: https://www.python.org/dev/peps/pep-0020/ [rfc4627]: http://www.ietf.org/rfc/rfc4627.txt [heroku-minified-json]: https://github.com/interagent/http-api-design#keep-json-minified-in-all-responses -[strftime]: http://docs.python.org/2/library/time.html#time.strftime +[strftime]: https://docs.python.org/3/library/time.html#time.strftime diff --git a/docs/api-guide/status-codes.md b/docs/api-guide/status-codes.md index 398c04804..f6ec3598f 100644 --- a/docs/api-guide/status-codes.md +++ b/docs/api-guide/status-codes.md @@ -50,6 +50,7 @@ This class of status code indicates that the client's request was successfully r HTTP_204_NO_CONTENT HTTP_205_RESET_CONTENT HTTP_206_PARTIAL_CONTENT + HTTP_207_MULTI_STATUS ## Redirection - 3xx @@ -86,6 +87,9 @@ The 4xx class of status code is intended for cases in which the client seems to HTTP_415_UNSUPPORTED_MEDIA_TYPE HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE HTTP_417_EXPECTATION_FAILED + HTTP_422_UNPROCESSABLE_ENTITY + HTTP_423_LOCKED + HTTP_424_FAILED_DEPENDENCY HTTP_428_PRECONDITION_REQUIRED HTTP_429_TOO_MANY_REQUESTS HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE @@ -101,6 +105,7 @@ Response status codes beginning with the digit "5" indicate cases in which the s HTTP_503_SERVICE_UNAVAILABLE HTTP_504_GATEWAY_TIMEOUT HTTP_505_HTTP_VERSION_NOT_SUPPORTED + HTTP_507_INSUFFICIENT_STORAGE HTTP_511_NETWORK_AUTHENTICATION_REQUIRED ## Helper functions diff --git a/docs/api-guide/testing.md b/docs/api-guide/testing.md index 69da7d105..13c746017 100644 --- a/docs/api-guide/testing.md +++ b/docs/api-guide/testing.md @@ -184,6 +184,110 @@ As usual CSRF validation will only apply to any session authenticated views. Th --- +# RequestsClient + +REST framework also includes a client for interacting with your application +using the popular Python library, `requests`. This may be useful if: + +* You are expecting to interface with the API primarily from another Python service, +and want to test the service at the same level as the client will see. +* You want to write tests in such a way that they can also be run against a staging or +live environment. (See "Live tests" below.) + +This exposes exactly the same interface as if you were using a requests session +directly. + + client = RequestsClient() + response = client.get('http://testserver/users/') + assert response.status_code == 200 + +Note that the requests client requires you to pass fully qualified URLs. + +## `RequestsClient` and working with the database + +The `RequestsClient` class is useful if you want to write tests that solely interact with the service interface. This is a little stricter than using the standard Django test client, as it means that all interactions should be via the API. + +If you're using `RequestsClient` you'll want to ensure that test setup, and results assertions are performed as regular API calls, rather than interacting with the database models directly. For example, rather than checking that `Customer.objects.count() == 3` you would list the customers endpoint, and ensure that it contains three records. + +## Headers & Authentication + +Custom headers and authentication credentials can be provided in the same way +as [when using a standard `requests.Session` instance](http://docs.python-requests.org/en/master/user/advanced/#session-objects). + + from requests.auth import HTTPBasicAuth + + client.auth = HTTPBasicAuth('user', 'pass') + client.headers.update({'x-test': 'true'}) + +## CSRF + +If you're using `SessionAuthentication` then you'll need to include a CSRF token +for any `POST`, `PUT`, `PATCH` or `DELETE` requests. + +You can do so by following the same flow that a JavaScript based client would use. +First make a `GET` request in order to obtain a CRSF token, then present that +token in the following request. + +For example... + + client = RequestsClient() + + # Obtain a CSRF token. + response = client.get('/homepage/') + assert response.status_code == 200 + csrftoken = response.cookies['csrftoken'] + + # Interact with the API. + response = client.post('/organisations/', json={ + 'name': 'MegaCorp', + 'status': 'active' + }, headers={'X-CSRFToken': csrftoken}) + assert response.status_code == 200 + +## Live tests + +With careful usage both the `RequestsClient` and the `CoreAPIClient` provide +the ability to write test cases that can run either in development, or be run +directly against your staging server or production environment. + +Using this style to create basic tests of a few core piece of functionality is +a powerful way to validate your live service. Doing so may require some careful +attention to setup and teardown to ensure that the tests run in a way that they +do not directly affect customer data. + +--- + +# CoreAPIClient + +The CoreAPIClient allows you to interact with your API using the Python +`coreapi` client library. + + # Fetch the API schema + client = CoreAPIClient() + schema = client.get('http://testserver/schema/') + + # Create a new organisation + params = {'name': 'MegaCorp', 'status': 'active'} + client.action(schema, ['organisations', 'create'], params) + + # Ensure that the organisation exists in the listing + data = client.action(schema, ['organisations', 'list']) + assert(len(data) == 1) + assert(data == [{'name': 'MegaCorp', 'status': 'active'}]) + +## Headers & Authentication + +Custom headers and authentication may be used with `CoreAPIClient` in a +similar way as with `RequestsClient`. + + from requests.auth import HTTPBasicAuth + + client = CoreAPIClient() + client.session.auth = HTTPBasicAuth('user', 'pass') + client.session.headers.update({'x-test': 'true'}) + +--- + # Test cases REST framework includes the following test case classes, that mirror the existing Django test case classes, but use `APIClient` instead of Django's default `Client`. @@ -197,7 +301,7 @@ REST framework includes the following test case classes, that mirror the existin You can use any of REST framework's test case classes as you would for the regular Django test case classes. The `self.client` attribute will be an `APIClient` instance. - from django.core.urlresolvers import reverse + from django.urls import reverse from rest_framework import status from rest_framework.test import APITestCase from myproject.apps.core.models import Account @@ -271,6 +375,6 @@ For example, to add support for using `format='html'` in test requests, you migh } [cite]: http://jacobian.org/writing/django-apps-with-buildout/#s-create-a-test-wrapper -[client]: https://docs.djangoproject.com/en/dev/topics/testing/tools/#the-test-client -[requestfactory]: https://docs.djangoproject.com/en/dev/topics/testing/advanced/#django.test.client.RequestFactory +[client]: https://docs.djangoproject.com/en/stable/topics/testing/tools/#the-test-client +[requestfactory]: https://docs.djangoproject.com/en/stable/topics/testing/advanced/#django.test.client.RequestFactory [configuration]: #configuration diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index 36753aafc..58578a23e 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -41,7 +41,7 @@ The default throttling policy may be set globally, using the `DEFAULT_THROTTLE_C The rate descriptions used in `DEFAULT_THROTTLE_RATES` may include `second`, `minute`, `hour` or `day` as the throttle period. You can also set the throttling policy on a per-view or per-viewset basis, -using the `APIView` class based views. +using the `APIView` class-based views. from rest_framework.response import Response from rest_framework.throttling import UserRateThrottle @@ -184,12 +184,14 @@ If the `.wait()` method is implemented and the request is throttled, then a `Ret The following is an example of a rate throttle, that will randomly throttle 1 in every 10 requests. + import random + class RandomRateThrottle(throttling.BaseThrottle): def allow_request(self, request, view): - return random.randint(1, 10) == 1 + return random.randint(1, 10) != 1 [cite]: https://dev.twitter.com/docs/error-codes-responses [permissions]: permissions.md [identifing-clients]: http://oxpedia.org/wiki/index.php?title=AppSuite:Grizzly#Multiple_Proxies_in_front_of_the_cluster -[cache-setting]: https://docs.djangoproject.com/en/dev/ref/settings/#caches -[cache-docs]: https://docs.djangoproject.com/en/dev/topics/cache/#setting-up-the-cache +[cache-setting]: https://docs.djangoproject.com/en/stable/ref/settings/#caches +[cache-docs]: https://docs.djangoproject.com/en/stable/topics/cache/#setting-up-the-cache diff --git a/docs/api-guide/validators.md b/docs/api-guide/validators.md index e54ebfc38..0e58c6fff 100644 --- a/docs/api-guide/validators.md +++ b/docs/api-guide/validators.md @@ -20,7 +20,7 @@ With `ModelForm` the validation is performed partially on the form, and partiall * It is easy to switch between using shortcut `ModelSerializer` classes and using explicit `Serializer` classes. Any validation behavior being used for `ModelSerializer` is simple to replicate. * Printing the `repr` of a serializer instance will show you exactly what validation rules it applies. There's no extra hidden validation behavior being called on the model instance. -When you're using `ModelSerializer` all of this is handled automatically for you. If you want to drop down to using a `Serializer` classes instead, then you need to define the validation rules explicitly. +When you're using `ModelSerializer` all of this is handled automatically for you. If you want to drop down to using `Serializer` classes instead, then you need to define the validation rules explicitly. #### Example @@ -61,9 +61,12 @@ It takes a single required argument, and an optional `messages` argument: * `queryset` *required* - This is the queryset against which uniqueness should be enforced. * `message` - The error message that should be used when validation fails. +* `lookup` - The lookup used to find an existing instance with the value being validated. Defaults to `'exact'`. This validator should be applied to *serializer fields*, like so: + from rest_framework.validators import UniqueValidator + slug = SlugField( max_length=100, validators=[UniqueValidator(queryset=BlogPost.objects.all())] @@ -80,6 +83,8 @@ It has two required arguments, and a single optional `messages` argument: The validator should be applied to *serializer classes*, like so: + from rest_framework.validators import UniqueTogetherValidator + class ExampleSerializer(serializers.Serializer): # ... class Meta: @@ -114,6 +119,8 @@ These validators can be used to enforce the `unique_for_date`, `unique_for_month The validator should be applied to *serializer classes*, like so: + from rest_framework.validators import UniqueForYearValidator + class ExampleSerializer(serializers.Serializer): # ... class Meta: @@ -146,17 +153,17 @@ The field will not be writable to the user, but the default value will still be #### Using with a hidden date field. -If you want the date field to be entirely hidden from the user, then use `HiddenField`. This field type does not accept user input, but instead always returns it's default value to the `validated_data` in the serializer. +If you want the date field to be entirely hidden from the user, then use `HiddenField`. This field type does not accept user input, but instead always returns its default value to the `validated_data` in the serializer. published = serializers.HiddenField(default=timezone.now) --- -**Note**: The `UniqueForValidation` classes always imposes an implicit constraint that the fields they are applied to are always treated as required. Fields with `default` values are an exception to this as they always supply a value even when omitted from user input. +**Note**: The `UniqueForValidation` classes impose an implicit constraint that the fields they are applied to are always treated as required. Fields with `default` values are an exception to this as they always supply a value even when omitted from user input. --- -# Advanced 'default' argument usage +# Advanced field defaults Validators that are applied across multiple fields in the serializer can sometimes require a field input that should not be provided by the API client, but that *is* available as input to the validator. @@ -183,11 +190,76 @@ It takes a single argument, which is the default value or callable that should b created_at = serializers.DateTimeField( read_only=True, - default=CreateOnlyDefault(timezone.now) + default=serializers.CreateOnlyDefault(timezone.now) ) --- +# Limitations of validators + +There are some ambiguous cases where you'll need to instead handle validation +explicitly, rather than relying on the default serializer classes that +`ModelSerializer` generates. + +In these cases you may want to disable the automatically generated validators, +by specifying an empty list for the serializer `Meta.validators` attribute. + +## Optional fields + +By default "unique together" validation enforces that all fields be +`required=True`. In some cases, you might want to explicit apply +`required=False` to one of the fields, in which case the desired behaviour +of the validation is ambiguous. + +In this case you will typically need to exclude the validator from the +serializer class, and instead write any validation logic explicitly, either +in the `.validate()` method, or else in the view. + +For example: + + class BillingRecordSerializer(serializers.ModelSerializer): + def validate(self, data): + # Apply custom validation either here, or in the view. + + class Meta: + fields = ('client', 'date', 'amount') + extra_kwargs = {'client': {'required': 'False'}} + validators = [] # Remove a default "unique together" constraint. + +## Updating nested serializers + +When applying an update to an existing instance, uniqueness validators will +exclude the current instance from the uniqueness check. The current instance +is available in the context of the uniqueness check, because it exists as +an attribute on the serializer, having initially been passed using +`instance=...` when instantiating the serializer. + +In the case of update operations on *nested* serializers there's no way of +applying this exclusion, because the instance is not available. + +Again, you'll probably want to explicitly remove the validator from the +serializer class, and write the code the for the validation constraint +explicitly, in a `.validate()` method, or in the view. + +## Debugging complex cases + +If you're not sure exactly what behavior a `ModelSerializer` class will +generate it is usually a good idea to run `manage.py shell`, and print +an instance of the serializer, so that you can inspect the fields and +validators that it automatically generates for you. + + >>> serializer = MyComplexModelSerializer() + >>> print(serializer) + class MyComplexModelSerializer: + my_fields = ... + +Also keep in mind that with complex cases it can often be better to explicitly +define your serializer classes, rather than relying on the default +`ModelSerializer` behavior. This involves a little more code, but ensures +that the resulting behavior is more transparent. + +--- + # Writing custom validators You can use any of Django's existing validators, or write your own custom validators. @@ -200,9 +272,15 @@ A validator may be any callable that raises a `serializers.ValidationError` on f if value % 2 != 0: raise serializers.ValidationError('This field must be an even number.') -## Class based +#### Field-level validation -To write a class based validator, use the `__call__` method. Class based validators are useful as they allow you to parameterize and reuse behavior. +You can specify custom field-level validation by adding `.validate_` methods +to your `Serializer` subclass. This is documented in the +[Serializer docs](http://www.django-rest-framework.org/api-guide/serializers/#field-level-validation) + +## Class-based + +To write a class-based validator, use the `__call__` method. Class-based validators are useful as they allow you to parameterize and reuse behavior. class MultipleOf(object): def __init__(self, base): @@ -215,11 +293,11 @@ To write a class based validator, use the `__call__` method. Class based validat #### Using `set_context()` -In some advanced cases you might want a validator to be passed the serializer field it is being used with as additional context. You can do so by declaring a `set_context` method on a class based validator. +In some advanced cases you might want a validator to be passed the serializer field it is being used with as additional context. You can do so by declaring a `set_context` method on a class-based validator. def set_context(self, serializer_field): # Determine if this is an update or a create operation. # In `__call__` we can then use that information to modify the validation behavior. self.is_update = serializer_field.parent.instance is not None -[cite]: https://docs.djangoproject.com/en/dev/ref/validators/ +[cite]: https://docs.djangoproject.com/en/stable/ref/validators/ diff --git a/docs/api-guide/versioning.md b/docs/api-guide/versioning.md index 54aa0170d..29672c96e 100644 --- a/docs/api-guide/versioning.md +++ b/docs/api-guide/versioning.md @@ -71,8 +71,8 @@ You can also set the versioning scheme on an individual view. Typically you won' The following settings keys are also used to control versioning: * `DEFAULT_VERSION`. The value that should be used for `request.version` when no versioning information is present. Defaults to `None`. -* `ALLOWED_VERSIONS`. If set, this value will restrict the set of versions that may be returned by the versioning scheme, and will raise an error if the provided version if not in this set. Note that the value used for the `DEFAULT_VERSION` setting is always considered to be part of the `ALLOWED_VERSIONS` set. Defaults to `None`. -* `VERSION_PARAM`. The string that should used for any versioning parameters, such as in the media type or URL query parameters. Defaults to `'version'`. +* `ALLOWED_VERSIONS`. If set, this value will restrict the set of versions that may be returned by the versioning scheme, and will raise an error if the provided version is not in this set. Note that the value used for the `DEFAULT_VERSION` setting is always considered to be part of the `ALLOWED_VERSIONS` set (unless it is `None`). Defaults to `None`. +* `VERSION_PARAM`. The string that should be used for any versioning parameters, such as in the media type or URL query parameters. Defaults to `'version'`. You can also set your versioning class plus those three values on a per-view or a per-viewset basis by defining your own versioning scheme and using the `default_version`, `allowed_versions` and `version_param` class variables. For example, if you want to use `URLPathVersioning`: diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index 291fe7376..c0c4f67e4 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -1,9 +1,9 @@ source: decorators.py views.py -# Class Based Views +# Class-based Views -> Django's class based views are a welcome departure from the old-style views. +> Django's class-based views are a welcome departure from the old-style views. > > — [Reinout van Rees][cite] @@ -73,6 +73,8 @@ The following methods are used by REST framework to instantiate the various plug ### .get_content_negotiator(self) +### .get_exception_handler(self) + ## API policy implementation methods The following methods are called before dispatching to the handler method. @@ -119,7 +121,7 @@ You won't typically need to override this method. # Function Based Views -> Saying [that Class based views] is always the superior solution is a mistake. +> Saying [that class-based views] is always the superior solution is a mistake. > > — [Nick Coghlan][cite2] @@ -127,7 +129,7 @@ REST framework also allows you to work with regular function based views. It pr ## @api_view() -**Signature:** `@api_view(http_method_names=['GET'])` +**Signature:** `@api_view(http_method_names=['GET'], exclude_from_schema=False)` The core of this functionality is the `api_view` decorator, which takes a list of HTTP methods that your view should respond to. For example, this is how you would write a very simple view that just manually returns some data: @@ -139,7 +141,7 @@ The core of this functionality is the `api_view` decorator, which takes a list o This view will use the default renderers, parsers, authentication classes etc specified in the [settings]. -By default only `GET` methods will be accepted. Other methods will respond with "405 Method Not Allowed". To alter this behavior, specify which methods the view allows, like so: +By default only `GET` methods will be accepted. Other methods will respond with "405 Method Not Allowed". To alter this behaviour, specify which methods the view allows, like so: @api_view(['GET', 'POST']) def hello_world(request): @@ -147,6 +149,13 @@ By default only `GET` methods will be accepted. Other methods will respond with return Response({"message": "Got some data!", "data": request.data}) return Response({"message": "Hello, world!"}) +You can also mark an API view as being omitted from any [auto-generated schema][schemas], +using the `exclude_from_schema` argument.: + + @api_view(['GET'], exclude_from_schema=True) + def api_docs(request): + ... + ## API policy decorators To override the default settings, REST framework provides a set of additional decorators which can be added to your views. These must come *after* (below) the `@api_view` decorator. For example, to create a view that uses a [throttle][throttling] to ensure it can only be called once per day by a particular user, use the `@throttle_classes` decorator, passing a list of throttle classes: @@ -178,3 +187,4 @@ Each of these decorators takes a single argument which must be a list or tuple o [cite2]: http://www.boredomandlaziness.org/2012/05/djangos-cbvs-are-not-mistake-but.html [settings]: settings.md [throttling]: throttling.md +[schemas]: schemas.md diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md index 0452a0f7b..e6df17f51 100644 --- a/docs/api-guide/viewsets.md +++ b/docs/api-guide/viewsets.md @@ -179,7 +179,7 @@ In order to use a `GenericViewSet` class you'll override the class and either mi The `ModelViewSet` class inherits from `GenericAPIView` and includes implementations for various actions, by mixing in the behavior of the various mixin classes. -The actions provided by the `ModelViewSet` class are `.list()`, `.retrieve()`, `.create()`, `.update()`, and `.destroy()`. +The actions provided by the `ModelViewSet` class are `.list()`, `.retrieve()`, `.create()`, `.update()`, `.partial_update()`, and `.destroy()`. #### Example diff --git a/docs/img/corejson-format.png b/docs/img/corejson-format.png new file mode 100644 index 000000000..36c197a0d Binary files /dev/null and b/docs/img/corejson-format.png differ diff --git a/docs/img/premium/machinalis-readme.png b/docs/img/premium/machinalis-readme.png new file mode 100644 index 000000000..cd98c23c7 Binary files /dev/null and b/docs/img/premium/machinalis-readme.png differ diff --git a/docs/img/premium/rollbar-readme.png b/docs/img/premium/rollbar-readme.png new file mode 100644 index 000000000..669612f72 Binary files /dev/null and b/docs/img/premium/rollbar-readme.png differ diff --git a/docs/img/premium/rover-readme.png b/docs/img/premium/rover-readme.png new file mode 100644 index 000000000..b8055d62e Binary files /dev/null and b/docs/img/premium/rover-readme.png differ diff --git a/docs/img/premium/sentry-readme.png b/docs/img/premium/sentry-readme.png new file mode 100644 index 000000000..5536ce52f Binary files /dev/null and b/docs/img/premium/sentry-readme.png differ diff --git a/docs/img/premium/stream-readme.png b/docs/img/premium/stream-readme.png new file mode 100644 index 000000000..fc3733c70 Binary files /dev/null and b/docs/img/premium/stream-readme.png differ diff --git a/docs/img/raml.png b/docs/img/raml.png new file mode 100644 index 000000000..87790dc48 Binary files /dev/null and b/docs/img/raml.png differ diff --git a/docs/img/rover.png b/docs/img/rover.png new file mode 100644 index 000000000..93aaca701 Binary files /dev/null and b/docs/img/rover.png differ diff --git a/docs/index.md b/docs/index.md index a2635c048..1760ce916 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,3 +1,22 @@ + +

@@ -38,23 +57,42 @@ Some reasons you might want to use REST framework: * [Serialization][serializers] that supports both [ORM][modelserializer-section] and [non-ORM][serializer-section] data sources. * Customizable all the way down - just use [regular function-based views][functionview-section] if you don't need the [more][generic-views] [powerful][viewsets] [features][routers]. * [Extensive documentation][index], and [great community support][group]. -* Used and trusted by large companies such as [Mozilla][mozilla] and [Eventbrite][eventbrite]. +* Used and trusted by internationally recognised companies including [Mozilla][mozilla], [Red Hat][redhat], [Heroku][heroku], and [Eventbrite][eventbrite]. --- -![Screenshot][image] +## Funding -**Above**: *Screenshot from the browsable API* +REST framework is a *collaboratively funded project*. If you use +REST framework commercially we strongly encourage you to invest in its +continued development by **[signing up for a paid plan][funding]**. + +The initial aim is to provide a single full-time position on REST framework. +*Every single sign-up makes a significant impact towards making that possible.* + +

+
+ +*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), [Machinalis](https://hello.machinalis.co.uk/), and [Rollbar](https://rollbar.com).* + +--- ## Requirements REST framework requires the following: * Python (2.7, 3.2, 3.3, 3.4, 3.5) -* Django (1.7+, 1.8, 1.9) +* Django (1.8, 1.9, 1.10) The following packages are optional: +* [coreapi][coreapi] (1.32.0+) - Schema generation support. * [Markdown][markdown] (2.1.0+) - Markdown support for the browsable API. * [django-filter][django-filter] (0.9.2+) - Filtering support. * [django-crispy-forms][django-crispy-forms] - Improved HTML display for filtering. @@ -147,10 +185,11 @@ The tutorial will walk you through the building blocks that make up REST framewo * [1 - Serialization][tut-1] * [2 - Requests & Responses][tut-2] -* [3 - Class based views][tut-3] +* [3 - Class-based views][tut-3] * [4 - Authentication & permissions][tut-4] * [5 - Relationships & hyperlinked APIs][tut-5] * [6 - Viewsets & routers][tut-6] +* [7 - Schemas & client libraries][tut-7] There is a live example API of the finished tutorial API for testing purposes, [available here][sandbox]. @@ -178,6 +217,7 @@ The API guide is your complete reference manual to all the functionality provide * [Versioning][versioning] * [Content negotiation][contentnegotiation] * [Metadata][metadata] +* [Schemas][schemas] * [Format suffixes][formatsuffixes] * [Returning URLs][reverse] * [Exceptions][exceptions] @@ -190,6 +230,7 @@ The API guide is your complete reference manual to all the functionality provide General guides to using REST framework. * [Documenting your API][documenting-your-api] +* [API Clients][api-clients] * [Internationalization][internationalization] * [AJAX, CSRF & CORS][ajax-csrf-cors] * [HTML & Forms][html-and-forms] @@ -203,7 +244,11 @@ General guides to using REST framework. * [3.1 Announcement][3.1-announcement] * [3.2 Announcement][3.2-announcement] * [3.3 Announcement][3.3-announcement] +* [3.4 Announcement][3.4-announcement] +* [3.5 Announcement][3.5-announcement] * [Kickstarter Announcement][kickstarter-announcement] +* [Mozilla Grant][mozilla-grant] +* [Funding][funding] * [Release Notes][release-notes] ## Development @@ -216,7 +261,7 @@ Framework. For support please see the [REST framework discussion group][group], try the `#restframework` channel on `irc.freenode.net`, search [the IRC archives][botbot], or raise a question on [Stack Overflow][stack-overflow], making sure to include the ['django-rest-framework'][django-rest-framework-tag] tag. -[Paid support is available][paid-support] from [DabApps][dabapps], and can include work on REST framework core, or support with building your REST framework API. Please [contact DabApps][contact-dabapps] if you'd like to discuss commercial support options. +For priority support please sign up for a [professional or premium sponsorship plan](https://fund.django-rest-framework.org/topics/funding/). For updates on REST framework development, you may also want to follow [the author][twitter] on Twitter. @@ -255,7 +300,10 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [mozilla]: http://www.mozilla.org/en-US/about/ +[redhat]: https://www.redhat.com/ +[heroku]: https://www.heroku.com/ [eventbrite]: https://www.eventbrite.co.uk/about/ +[coreapi]: http://pypi.python.org/pypi/coreapi/ [markdown]: http://pypi.python.org/pypi/Markdown/ [django-filter]: http://pypi.python.org/pypi/django-filter [django-crispy-forms]: https://github.com/maraujop/django-crispy-forms @@ -269,6 +317,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [modelserializer-section]: api-guide/serializers#modelserializer [functionview-section]: api-guide/views#function-based-views [sandbox]: http://restframework.herokuapp.com/ +[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors [quickstart]: tutorial/quickstart.md [tut-1]: tutorial/1-serialization.md @@ -277,6 +326,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [tut-4]: tutorial/4-authentication-and-permissions.md [tut-5]: tutorial/5-relationships-and-hyperlinked-apis.md [tut-6]: tutorial/6-viewsets-and-routers.md +[tut-7]: tutorial/7-schemas-and-client-libraries.md [request]: api-guide/requests.md [response]: api-guide/responses.md @@ -298,6 +348,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [versioning]: api-guide/versioning.md [contentnegotiation]: api-guide/content-negotiation.md [metadata]: api-guide/metadata.md +[schemas]: api-guide/schemas.md [formatsuffixes]: api-guide/format-suffixes.md [reverse]: api-guide/reverse.md [exceptions]: api-guide/exceptions.md @@ -306,6 +357,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [settings]: api-guide/settings.md [documenting-your-api]: topics/documenting-your-api.md +[api-clients]: topics/api-clients.md [internationalization]: topics/internationalization.md [ajax-csrf-cors]: topics/ajax-csrf-cors.md [html-and-forms]: topics/html-and-forms.md @@ -319,7 +371,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [3.1-announcement]: topics/3.1-announcement.md [3.2-announcement]: topics/3.2-announcement.md [3.3-announcement]: topics/3.3-announcement.md +[3.4-announcement]: topics/3.4-announcement.md +[3.5-announcement]: topics/3.5-announcement.md [kickstarter-announcement]: topics/kickstarter-announcement.md +[mozilla-grant]: topics/mozilla-grant.md [funding]: topics/funding.md [release-notes]: topics/release-notes.md diff --git a/docs/topics/2.2-announcement.md b/docs/topics/2.2-announcement.md index e6220f427..ca4ed2efa 100644 --- a/docs/topics/2.2-announcement.md +++ b/docs/topics/2.2-announcement.md @@ -147,10 +147,10 @@ When using a serializer with a `HyperlinkedRelatedField` or `HyperlinkedIdentity From version 2.2 onwards, serializers with hyperlinked relationships *always* require a `'request'` key to be supplied in the context dictionary. The implicit behavior will continue to function, but its use will raise a `PendingDeprecationWarning`. [xordoquy]: https://github.com/xordoquy -[django-python-3]: https://docs.djangoproject.com/en/dev/faq/install/#can-i-use-django-with-python-3 -[porting-python-3]: https://docs.djangoproject.com/en/dev/topics/python3/ -[python-compat]: https://docs.djangoproject.com/en/dev/releases/1.5/#python-compatibility -[django-deprecation-policy]: https://docs.djangoproject.com/en/dev/internals/release-process/#internal-release-deprecation-policy +[django-python-3]: https://docs.djangoproject.com/en/stable/faq/install/#can-i-use-django-with-python-3 +[porting-python-3]: https://docs.djangoproject.com/en/stable/topics/python3/ +[python-compat]: https://docs.djangoproject.com/en/stable/releases/1.5/#python-compatibility +[django-deprecation-policy]: https://docs.djangoproject.com/en/stable/internals/release-process/#internal-release-deprecation-policy [credits]: http://www.django-rest-framework.org/topics/credits [mailing-list]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework [django-rest-framework-docs]: https://github.com/marcgibbons/django-rest-framework-docs diff --git a/docs/topics/2.3-announcement.md b/docs/topics/2.3-announcement.md index 21d9f1dbc..d9bab39dc 100644 --- a/docs/topics/2.3-announcement.md +++ b/docs/topics/2.3-announcement.md @@ -6,7 +6,7 @@ REST framework 2.3 makes it even quicker and easier to build your Web APIs. The 2.3 release introduces the [ViewSet][viewset] and [Router][router] classes. -A viewset is simply a type of class based view that allows you to group multiple views into a single common class. +A viewset is simply a type of class-based view that allows you to group multiple views into a single common class. Routers allow you to automatically determine the URLconf for your viewset classes. diff --git a/docs/topics/2.4-announcement.md b/docs/topics/2.4-announcement.md index 4ca35290c..96f68c865 100644 --- a/docs/topics/2.4-announcement.md +++ b/docs/topics/2.4-announcement.md @@ -39,7 +39,7 @@ Then run the `runtests.py` script. ./runtests.py -The new test runner also includes [flake8](https://flake8.readthedocs.org) code linting, which should help keep our coding style consistent. +The new test runner also includes [flake8](https://flake8.readthedocs.io) code linting, which should help keep our coding style consistent. #### Test runner flags @@ -162,7 +162,7 @@ The next planned release will be 3.0, featuring an improved and simplified seria Once again, many thanks to all the generous [backers and sponsors][kickstarter-sponsors] who've helped make this possible! -[lts-releases]: https://docs.djangoproject.com/en/dev/internals/release-process/#long-term-support-lts-releases +[lts-releases]: https://docs.djangoproject.com/en/stable/internals/release-process/#long-term-support-lts-releases [2-4-release-notes]: release-notes#240 [view-name-and-description-settings]: ../api-guide/settings#view-names-and-descriptions [client-ip-identification]: ../api-guide/throttling#how-clients-are-identified diff --git a/docs/topics/3.0-announcement.md b/docs/topics/3.0-announcement.md index 89f9e8001..f09788f56 100644 --- a/docs/topics/3.0-announcement.md +++ b/docs/topics/3.0-announcement.md @@ -426,7 +426,7 @@ There are four methods that can be overridden, depending on what functionality y * `.to_internal_value()` - Override this to support deserialization, for write operations. * `.create()` and `.update()` - Override either or both of these to support saving instances. -Because this class provides the same interface as the `Serializer` class, you can use it with the existing generic class based views exactly as you would for a regular `Serializer` or `ModelSerializer`. +Because this class provides the same interface as the `Serializer` class, you can use it with the existing generic class-based views exactly as you would for a regular `Serializer` or `ModelSerializer`. The only difference you'll notice when doing so is the `BaseSerializer` classes will not generate HTML forms in the browsable API. This is because the data they return does not include all the field information that would allow each field to be rendered into a suitable HTML input. @@ -801,7 +801,7 @@ This change means that you can now easily customize the style of error responses ## The metadata API -Behavior for dealing with `OPTIONS` requests was previously built directly into the class based views. This has now been properly separated out into a Metadata API that allows the same pluggable style as other API policies in REST framework. +Behavior for dealing with `OPTIONS` requests was previously built directly into the class-based views. This has now been properly separated out into a Metadata API that allows the same pluggable style as other API policies in REST framework. This makes it far easier to use a different style for `OPTIONS` responses throughout your API, and makes it possible to create third-party metadata policies. @@ -870,7 +870,7 @@ The `COMPACT_JSON` setting has been added, and can be used to revert this behavi #### File fields as URLs -The `FileField` and `ImageField` classes are now represented as URLs by default. You should ensure you set Django's [standard `MEDIA_URL` setting](https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-MEDIA_URL) appropriately, and ensure your application [serves the uploaded files](https://docs.djangoproject.com/en/dev/howto/static-files/#serving-uploaded-files-in-development). +The `FileField` and `ImageField` classes are now represented as URLs by default. You should ensure you set Django's [standard `MEDIA_URL` setting](https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-MEDIA_URL) appropriately, and ensure your application [serves the uploaded files](https://docs.djangoproject.com/en/stable/howto/static-files/#serving-uploaded-files-in-development). You can revert this behavior, and display filenames in the representation by using the `UPLOADED_FILES_USE_URL` settings key: @@ -894,11 +894,11 @@ If the request is omitted from the context, the returned URLs will be of the for The custom `X-Throttle-Wait-Second` header has now been dropped in favor of the standard `Retry-After` header. You can revert this behavior if needed by writing a custom exception handler for your application. -#### Date and time objects as ISO-8859-1 strings in serializer data. +#### Date and time objects as ISO-8601 strings in serializer data. Date and Time objects are now coerced to strings by default in the serializer output. Previously they were returned as `Date`, `Time` and `DateTime` objects, and later coerced to strings by the renderer. -You can modify this behavior globally by settings the existing `DATE_FORMAT`, `DATETIME_FORMAT` and `TIME_FORMAT` settings keys. Setting these values to `None` instead of their default value of `'iso-8859-1'` will result in native objects being returned in serializer data. +You can modify this behavior globally by settings the existing `DATE_FORMAT`, `DATETIME_FORMAT` and `TIME_FORMAT` settings keys. Setting these values to `None` instead of their default value of `'iso-8601'` will result in native objects being returned in serializer data. REST_FRAMEWORK = { # Return native `Date` and `Time` objects in `serializer.data` @@ -962,4 +962,4 @@ You can follow development on the GitHub site, where we use [milestones to indic [kickstarter]: http://kickstarter.com/projects/tomchristie/django-rest-framework-3 [sponsors]: http://www.django-rest-framework.org/topics/kickstarter-announcement/#sponsors [mixins.py]: https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/mixins.py -[django-localization]: https://docs.djangoproject.com/en/dev/topics/i18n/translation/#localization-how-to-create-language-files +[django-localization]: https://docs.djangoproject.com/en/stable/topics/i18n/translation/#localization-how-to-create-language-files diff --git a/docs/topics/3.1-announcement.md b/docs/topics/3.1-announcement.md index 80d4007eb..6cca40665 100644 --- a/docs/topics/3.1-announcement.md +++ b/docs/topics/3.1-announcement.md @@ -30,7 +30,7 @@ Note that as a result of this work a number of settings keys and generic view at Until now, there has only been a single built-in pagination style in REST framework. We now have page, limit/offset and cursor based schemes included by default. -The cursor based pagination scheme is particularly smart, and is a better approach for clients iterating through large or frequently changing result sets. The scheme supports paging against non-unique indexes, by using both cursor and limit/offset information. It also allows for both forward and reverse cursor pagination. Much credit goes to David Cramer for [this blog post](http://cramer.io/2011/03/08/building-cursors-for-the-disqus-api/) on the subject. +The cursor based pagination scheme is particularly smart, and is a better approach for clients iterating through large or frequently changing result sets. The scheme supports paging against non-unique indexes, by using both cursor and limit/offset information. It also allows for both forward and reverse cursor pagination. Much credit goes to David Cramer for [this blog post](http://cramer.io/2011/03/08/building-cursors-for-the-disqus-api) on the subject. #### Pagination controls in the browsable API. @@ -110,7 +110,7 @@ When per-request internationalization is enabled, client requests will respect t "detail": "No se ha podido satisfacer la solicitud de cabecera de Accept." } -Note that the structure of the error responses is still the same. We still have a `details` key in the response. If needed you can modify this behavior too, by using a [custom exception handler][custom-exception-handler]. +Note that the structure of the error responses is still the same. We still have a `detail` key in the response. If needed you can modify this behavior too, by using a [custom exception handler][custom-exception-handler]. We include built-in translations both for standard exception cases, and for serializer validation errors. @@ -153,7 +153,7 @@ For more information, see the documentation on [customizing field mappings][cust We've now moved a number of packages out of the core of REST framework, and into separately installable packages. If you're currently using these you don't need to worry, you simply need to `pip install` the new packages, and change any import paths. -We're making this change in order to help distribute the maintainance workload, and keep better focus of the core essentials of the framework. +We're making this change in order to help distribute the maintenance workload, and keep better focus of the core essentials of the framework. The change also means we can be more flexible with which external packages we recommend. For example, the excellently maintained [Django OAuth toolkit](https://github.com/evonove/django-oauth-toolkit) has now been promoted as our recommended option for integrating OAuth support. diff --git a/docs/topics/3.2-announcement.md b/docs/topics/3.2-announcement.md index f0cc09a46..dc439f110 100644 --- a/docs/topics/3.2-announcement.md +++ b/docs/topics/3.2-announcement.md @@ -54,7 +54,7 @@ The following pagination view attributes and settings have been moved into attri * `view.max_paginate_by` - Use `paginator.max_page_size` instead. * `settings.PAGINATE_BY` - Use `paginator.page_size` instead. * `settings.PAGINATE_BY_PARAM` - Use `paginator.page_size_query_param` instead. -* `settings.MAX_PAGINATE_BY` - Use `max_page_size` instead. +* `settings.MAX_PAGINATE_BY` - Use `paginator.max_page_size` instead. ## Modifications to list behaviors diff --git a/docs/topics/3.4-announcement.md b/docs/topics/3.4-announcement.md new file mode 100644 index 000000000..438192c9c --- /dev/null +++ b/docs/topics/3.4-announcement.md @@ -0,0 +1,194 @@ + + +# Django REST framework 3.4 + +The 3.4 release is the first in a planned series that will be addressing schema +generation, hypermedia support, API clients, and finally realtime support. + +--- + +## Funding + +The 3.4 release has been made possible a recent [Mozilla grant][moss], and by our +[collaborative funding model][funding]. If you use REST framework commercially, and would +like to see this work continue, we strongly encourage you to invest in its +continued development by **[signing up for a paid plan][funding]**. + +The initial aim is to provide a single full-time position on REST framework. +Right now we're over 60% of the way towards achieving that. +*Every single sign-up makes a significant impact.* + + +
+ +*Many thanks to all our [awesome sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), and [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf).* + +--- + +## Schemas & client libraries + +REST framework 3.4 brings built-in support for generating API schemas. + +We provide this support by using [Core API][core-api], a Document Object Model +for describing APIs. + +Because Core API represents the API schema in an format-independent +manner, we're able to render the Core API `Document` object into many different +schema formats, by allowing the renderer class to determine how the internal +representation maps onto the external schema format. + +This approach should also open the door to a range of auto-generated API +documentation options in the future, by rendering the `Document` object into +HTML documentation pages. + +Alongside the built-in schema support, we're also now providing the following: + +* A [command line tool][command-line-client] for interacting with APIs. +* A [Python client library][client-library] for interacting with APIs. + +These API clients are dynamically driven, and able to interact with any API +that exposes a supported schema format. + +Dynamically driven clients allow you to interact with an API at an application +layer interface, rather than a network layer interface, while still providing +the benefits of RESTful Web API design. + +We're expecting to expand the range of languages that we provide client libraries +for over the coming months. + +Further work on maturing the API schema support is also planned, including +documentation on supporting file upload and download, and improved support for +documentation generation and parameter annotation. + +--- + +Current support for schema formats is as follows: + +Name | Support | PyPI package +---------------------------------|-------------------------------------|-------------------------------- +[Core JSON][core-json] | Schema generation & client support. | Built-in support in `coreapi`. +[Swagger / OpenAPI][swagger] | Schema generation & client support. | The `openapi-codec` package. +[JSON Hyper-Schema][hyperschema] | Currently client support only. | The `hyperschema-codec` package. +[API Blueprint][api-blueprint] | Not yet available. | Not yet available. + +--- + +You can read more about any of this new functionality in the following: + +* New tutorial section on [schemas & client libraries][tut-7]. +* Documentation page on [schema generation][schema-generation]. +* Topic page on [API clients][api-clients]. + +It is also worth noting that Marc Gibbons is currently working towards a 2.0 release of +the popular Django REST Swagger package, which will tie in with our new built-in support. + +--- + +## Supported versions + +The 3.4.0 release adds support for Django 1.10. + +The following versions of Python and Django are now supported: + +* Django versions 1.8, 1.9, and 1.10. +* Python versions 2.7, 3.2(\*), 3.3(\*), 3.4, 3.5. + +(\*) Note that Python 3.2 and 3.3 are not supported from Django 1.9 onwards. + +--- + +## Deprecations and changes + +The 3.4 release includes very limited deprecation or behavioral changes, and +should present a straightforward upgrade. + +### Use fields or exclude on serializer classes. + +The following change in 3.3.0 is now escalated from "pending deprecation" to +"deprecated". Its usage will continue to function but will raise warnings: + +`ModelSerializer` and `HyperlinkedModelSerializer` should include either a `fields` +option, or an `exclude` option. The `fields = '__all__'` shortcut may be used +to explicitly include all fields. + +### Microsecond precision when returning time or datetime. + +Using the default JSON renderer and directly returning a `datetime` or `time` +instance will now render with microsecond precision (6 digits), rather than +millisecond precision (3 digits). This makes the output format consistent with the +default string output of `serializers.DateTimeField` and `serializers.TimeField`. + +This change *does not affect the default behavior when using serializers*, +which is to serialize `datetime` and `time` instances into strings with +microsecond precision. + +The serializer behavior can be modified if needed, using the `DATETIME_FORMAT` +and `TIME_FORMAT` settings. + +The renderer behavior can be modified by setting a custom `encoder_class` +attribute on a `JSONRenderer` subclass. + +### Relational choices no longer displayed in OPTIONS requests. + +Making an `OPTIONS` request to views that have a serializer choice field +will result in a list of the available choices being returned in the response. + +In cases where there is a relational field, the previous behavior would be +to return a list of available instances to choose from for that relational field. + +In order to minimise exposed information the behavior now is to *not* return +choices information for relational fields. + +If you want to override this new behavior you'll need to [implement a custom +metadata class][metadata]. + +See [issue #3751][gh3751] for more information on this behavioral change. + +--- + +## Other improvements + +This release includes further work from a huge number of [pull requests and issues][milestone]. + +Many thanks to all our contributors who've been involved in the release, either through raising issues, giving feedback, improving the documentation, or suggesting and implementing code changes. + +The full set of itemized release notes [are available here][release-notes]. + +[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors +[moss]: mozilla-grant.md +[funding]: funding.md +[core-api]: http://www.coreapi.org/ +[command-line-client]: api-clients#command-line-client +[client-library]: api-clients#python-client-library +[core-json]: http://www.coreapi.org/specification/encoding/#core-json-encoding +[swagger]: https://openapis.org/specification +[hyperschema]: http://json-schema.org/latest/json-schema-hypermedia.html +[api-blueprint]: https://apiblueprint.org/ +[tut-7]: ../../tutorial/7-schemas-and-client-libraries/ +[schema-generation]: ../../api-guide/schemas/ +[api-clients]: api-clients.md +[milestone]: https://github.com/tomchristie/django-rest-framework/milestone/35 +[release-notes]: release-notes#34 +[metadata]: ../../api-guide/metadata/#custom-metadata-classes +[gh3751]: https://github.com/tomchristie/django-rest-framework/issues/3751 diff --git a/docs/topics/3.5-announcement.md b/docs/topics/3.5-announcement.md new file mode 100644 index 000000000..ea50b2418 --- /dev/null +++ b/docs/topics/3.5-announcement.md @@ -0,0 +1,266 @@ + + +# Django REST framework 3.5 + +The 3.5 release is the second in a planned series that is addressing schema +generation, hypermedia support, API client libraries, and finally realtime support. + +--- + +## Funding + +The 3.5 release would not have been possible without our [collaborative funding model][funding]. +If you use REST framework commercially and would like to see this work continue, +we strongly encourage you to invest in its continued development by +**[signing up for a paid plan][funding]**. + + +
+ +*Many thanks to all our [sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), and [Machinalis](http://www.machinalis.com/#services).* + +--- + +## Improved schema generation + +Docstrings on views are now pulled through into schema definitions, allowing +you to [use the schema definition to document your API][schema-docs]. + +There is now also a shortcut function, `get_schema_view()`, which makes it easier to +[adding schema views][schema-view] to your API. + +For example, to include a swagger schema to your API, you would do the following: + +* Run `pip install django-rest-swagger`. + +* Add `'rest_framework_swagger'` to your `INSTALLED_APPS` setting. + +* Include the schema view in your URL conf: + +```py +from rest_framework.schemas import get_schema_view +from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer + +schema_view = get_schema_view( + title='Example API', + renderer_classes=[OpenAPIRenderer, SwaggerUIRenderer] +) + +urlpatterns = [ + url(r'^swagger/$', schema_view), + ... +] +``` + +There have been a large number of fixes to the schema generation. These should +resolve issues for anyone using the latest version of the `django-rest-swagger` +package. + +Some of these changes do affect the resulting schema structure, +so if you're already using schema generation you should make sure to review +[the deprecation notes](#deprecations), particularly if you're currently using +a dynamic client library to interact with your API. + +Finally, we're also now exposing the schema generation as a +[publicly documented API][schema-generation-api], allowing you to more easily +override the behaviour. + +## Requests test client + +You can now test your project using the `requests` library. + +This exposes exactly the same interface as if you were using a standard +requests session instance. + + client = RequestsClient() + response = client.get('http://testserver/users/') + assert response.status_code == 200 + +Rather than sending any HTTP requests to the network, this interface will +coerce all outgoing requests into WSGI, and call into your application directly. + +## Core API client + +You can also now test your project by interacting with it using the `coreapi` +client library. + + # Fetch the API schema + client = CoreAPIClient() + schema = client.get('http://testserver/schema/') + + # Create a new organisation + params = {'name': 'MegaCorp', 'status': 'active'} + client.action(schema, ['organisations', 'create'], params) + + # Ensure that the organisation exists in the listing + data = client.action(schema, ['organisations', 'list']) + assert(len(data) == 1) + assert(data == [{'name': 'MegaCorp', 'status': 'active'}]) + +Again, this will call directly into the application using the WSGI interface, +rather than making actual network calls. + +This is a good option if you are planning for clients to mainly interact with +your API using the `coreapi` client library, or some other auto-generated client. + +## Live tests + +One interesting aspect of both the `requests` client and the `coreapi` client +is that they allow you to write tests in such a way that they can also be made +to run against a live service. + +By switching the WSGI based client instances to actual instances of `requests.Session` +or `coreapi.Client` you can have the test cases make actual network calls. + +Being able to write test cases that can exercise your staging or production +environment is a powerful tool. However in order to do this, you'll need to pay +close attention to how you handle setup and teardown to ensure a strict isolation +of test data from other live or staging data. + +## RAML support + +We now have preliminary support for [RAML documentation generation][django-rest-raml]. + +![RAML Example][raml-image] + +Further work on the encoding and documentation generation is planned, in order to +make features such as the 'Try it now' support available at a later date. + +This work also now means that you can use the Core API client libraries to interact +with APIs that expose a RAML specification. The [RAML codec][raml-codec] gives some examples of +interacting with the Spotify API in this way. + +## Validation codes + +Exceptions raised by REST framework now include short code identifiers. +When used together with our customizable error handling, this now allows you to +modify the style of API error messages. + +As an example, this allows for the following style of error responses: + + { + "message": "You do not have permission to perform this action.", + "code": "permission_denied" + } + +This is particularly useful with validation errors, which use appropriate +codes to identify differing kinds of failure... + + { + "name": {"message": "This field is required.", "code": "required"}, + "age": {"message": "A valid integer is required.", "code": "invalid"} + } + +## Client upload & download support + +The Python `coreapi` client library and the Core API command line tool both +now fully support file [uploads][uploads] and [downloads][downloads]. + +--- + +## Deprecations + +### Generating schemas from Router + +The router arguments for generating a schema view, such as `schema_title`, +are now pending deprecation. + +Instead of using `DefaultRouter(schema_title='Example API')`, you should use +the `get_schema_view()` function, and include the view in your URL conf. + +Make sure to include the view before your router urls. For example: + + from rest_framework.schemas import get_schema_view + from my_project.routers import router + + schema_view = get_schema_view(title='Example API') + + urlpatterns = [ + url('^$', schema_view), + url(r'^', include(router.urls)), + ] + +### Schema path representations + +The `'pk'` identifier in schema paths is now mapped onto the actually model field +name by default. This will typically be `'id'`. + +This gives a better external representation for schemas, with less implementation +detail being exposed. It also reflects the behaviour of using a ModelSerializer +class with `fields = '__all__'`. + +You can revert to the previous behaviour by setting `'SCHEMA_COERCE_PATH_PK': False` +in the REST framework settings. + +### Schema action name representations + +The internal `retrieve()` and `destroy()` method names are now coerced to an +external representation of `read` and `delete`. + +You can revert to the previous behaviour by setting `'SCHEMA_COERCE_METHOD_NAMES': {}` +in the REST framework settings. + +### DjangoFilterBackend + +The functionality of the built-in `DjangoFilterBackend` is now completely +included by the `django-filter` package. + +You should change your imports and REST framework filter settings as follows: + +* `rest_framework.filters.DjangoFilterBackend` becomes `django_filters.rest_framework.DjangoFilterBackend`. +* `rest_framework.filters.FilterSet` becomes `django_filters.rest_framework.FilterSet`. + +The existing imports will continue to work but are now pending deprecation. + +### CoreJSON media type + +The media type for `CoreJSON` is now `application/json+coreapi`, rather than +the previous `application/vnd.json+coreapi`. This brings it more into line with +other custom media types, such as those used by Swagger and RAML. + +The clients currently accept either media type. The old style-media type will +be deprecated at a later date. + +### ModelSerializer 'fields' and 'exclude' + +ModelSerializer and HyperlinkedModelSerializer must include either a fields +option, or an exclude option. The `fields = '__all__'` shortcut may be used to +explicitly include all fields. + +Failing to set either `fields` or `exclude` raised a pending deprecation warning +in version 3.3 and raised a deprecation warning in 3.4. Its usage is now mandatory. + +--- + +[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors +[funding]: funding.md +[uploads]: http://core-api.github.io/python-client/api-guide/utils/#file +[downloads]: http://core-api.github.io/python-client/api-guide/codecs/#downloadcodec +[schema-generation-api]: ../api-guide/schemas/#schemagenerator +[schema-docs]: ../api-guide/schemas/#schemas-as-documentation +[schema-view]: ../api-guide/schemas/#the-get_schema_view-shortcut +[django-rest-raml]: https://github.com/tomchristie/django-rest-raml +[raml-image]: ../img/raml.png +[raml-codec]: https://github.com/core-api/python-raml-codec diff --git a/docs/topics/ajax-csrf-cors.md b/docs/topics/ajax-csrf-cors.md index ad88810da..4960e0881 100644 --- a/docs/topics/ajax-csrf-cors.md +++ b/docs/topics/ajax-csrf-cors.md @@ -35,7 +35,7 @@ The best way to deal with CORS in REST framework is to add the required response [cite]: http://www.codinghorror.com/blog/2008/10/preventing-csrf-and-xsrf-attacks.html [csrf]: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF) -[csrf-ajax]: https://docs.djangoproject.com/en/dev/ref/csrf/#ajax +[csrf-ajax]: https://docs.djangoproject.com/en/stable/ref/csrf/#ajax [cors]: http://www.w3.org/TR/cors/ [ottoyiu]: https://github.com/ottoyiu/ [django-cors-headers]: https://github.com/ottoyiu/django-cors-headers/ diff --git a/docs/topics/api-clients.md b/docs/topics/api-clients.md new file mode 100644 index 000000000..fa7b60d3e --- /dev/null +++ b/docs/topics/api-clients.md @@ -0,0 +1,325 @@ +# API Clients + +An API client handles the underlying details of how network requests are made +and how responses are decoded. They present the developer with an application +interface to work against, rather than working directly with the network interface. + +The API clients documented here are not restricted to APIs built with Django REST framework. + They can be used with any API that exposes a supported schema format. + +For example, [the Heroku platform API][heroku-api] exposes a schema in the JSON +Hyperschema format. As a result, the Core API command line client and Python +client library can be [used to interact with the Heroku API][heroku-example]. + +## Client-side Core API + +[Core API][core-api] is a document specification that can be used to describe APIs. It can +be used either server-side, as is done with REST framework's [schema generation][schema-generation], +or used client-side, as described here. + +When used client-side, Core API allows for *dynamically driven client libraries* +that can interact with any API that exposes a supported schema or hypermedia +format. + +Using a dynamically driven client has a number of advantages over interacting +with an API by building HTTP requests directly. + +#### More meaningful interaction + +API interactions are presented in a more meaningful way. You're working at +the application interface layer, rather than the network interface layer. + +#### Resilience & evolvability + +The client determines what endpoints are available, what parameters exist +against each particular endpoint, and how HTTP requests are formed. + +This also allows for a degree of API evolvability. URLs can be modified +without breaking existing clients, or more efficient encodings can be used +on-the-wire, with clients transparently upgrading. + +#### Self-descriptive APIs + +A dynamically driven client is able to present documentation on the API to the +end user. This documentation allows the user to discover the available endpoints +and parameters, and better understand the API they are working with. + +Because this documentation is driven by the API schema it will always be fully +up to date with the most recently deployed version of the service. + +--- + +# Command line client + +The command line client allows you to inspect and interact with any API that +exposes a supported schema format. + +## Getting started + +To install the Core API command line client, use `pip`. + +Note that the command-line client is a separate package to the +python client library. Make sure to install `coreapi-cli`. + + $ pip install coreapi-cli + +To start inspecting and interacting with an API the schema must first be loaded +from the network. + + $ coreapi get http://api.example.org/ + + snippets: { + create(code, [title], [linenos], [language], [style]) + destroy(pk) + highlight(pk) + list([page]) + partial_update(pk, [title], [code], [linenos], [language], [style]) + retrieve(pk) + update(pk, code, [title], [linenos], [language], [style]) + } + users: { + list([page]) + retrieve(pk) + } + +This will then load the schema, displaying the resulting `Document`. This +`Document` includes all the available interactions that may be made against the API. + +To interact with the API, use the `action` command. This command requires a list +of keys that are used to index into the link. + + $ coreapi action users list + [ + { + "url": "http://127.0.0.1:8000/users/2/", + "id": 2, + "username": "aziz", + "snippets": [] + }, + ... + ] + +To inspect the underlying HTTP request and response, use the `--debug` flag. + + $ coreapi action users list --debug + > GET /users/ HTTP/1.1 + > Accept: application/vnd.coreapi+json, */* + > Authorization: Basic bWF4Om1heA== + > Host: 127.0.0.1 + > User-Agent: coreapi + < 200 OK + < Allow: GET, HEAD, OPTIONS + < Content-Type: application/json + < Date: Thu, 30 Jun 2016 10:51:46 GMT + < Server: WSGIServer/0.1 Python/2.7.10 + < Vary: Accept, Cookie + < + < [{"url":"http://127.0.0.1/users/2/","id":2,"username":"aziz","snippets":[]},{"url":"http://127.0.0.1/users/3/","id":3,"username":"amy","snippets":["http://127.0.0.1/snippets/3/"]},{"url":"http://127.0.0.1/users/4/","id":4,"username":"max","snippets":["http://127.0.0.1/snippets/4/","http://127.0.0.1/snippets/5/","http://127.0.0.1/snippets/6/","http://127.0.0.1/snippets/7/"]},{"url":"http://127.0.0.1/users/5/","id":5,"username":"jose","snippets":[]},{"url":"http://127.0.0.1/users/6/","id":6,"username":"admin","snippets":["http://127.0.0.1/snippets/1/","http://127.0.0.1/snippets/2/"]}] + + [ + ... + ] + +Some actions may include optional or required parameters. + + $ coreapi action users create --param username=example + +When using `--param`, the type of the input will be determined automatically. + +If you want to be more explicit about the parameter type then use `--data` for +any null, numeric, boolean, list, or object inputs, and use `--string` for string inputs. + + $ coreapi action users edit --string username=tomchristie --data is_admin=true + +## Authentication & headers + +The `credentials` command is used to manage the request `Authentication:` header. +Any credentials added are always linked to a particular domain, so as to ensure +that credentials are not leaked across differing APIs. + +The format for adding a new credential is: + + $ coreapi credentials add + +For instance: + + $ coreapi credentials add api.example.org "Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b" + +The optional `--auth` flag also allows you to add specific types of authentication, +handling the encoding for you. Currently only `"basic"` is supported as an option here. +For example: + + $ coreapi credentials add api.example.org tomchristie:foobar --auth basic + +You can also add specific request headers, using the `headers` command: + + $ coreapi headers add api.example.org x-api-version 2 + +For more information and a listing of the available subcommands use `coreapi +credentials --help` or `coreapi headers --help`. + +## Codecs + +By default the command line client only includes support for reading Core JSON +schemas, however it includes a plugin system for installing additional codecs. + + $ pip install openapi-codec jsonhyperschema-codec hal-codec + $ coreapi codecs show + Codecs + corejson application/vnd.coreapi+json encoding, decoding + hal application/hal+json encoding, decoding + openapi application/openapi+json encoding, decoding + jsonhyperschema application/schema+json decoding + json application/json data + text text/* data + +## Utilities + +The command line client includes functionality for bookmarking API URLs +under a memorable name. For example, you can add a bookmark for the +existing API, like so... + + $ coreapi bookmarks add accountmanagement + +There is also functionality for navigating forward or backward through the +history of which API URLs have been accessed. + + $ coreapi history show + $ coreapi history back + +For more information and a listing of the available subcommands use +`coreapi bookmarks --help` or `coreapi history --help`. + +## Other commands + +To display the current `Document`: + + $ coreapi show + +To reload the current `Document` from the network: + + $ coreapi reload + +To load a schema file from disk: + + $ coreapi load my-api-schema.json --format corejson + +To dump the current document to console in a given format: + + $ coreapi dump --format openapi + +To remove the current document, along with all currently saved history, +credentials, headers and bookmarks: + + $ coreapi clear + +--- + +# Python client library + +The `coreapi` Python package allows you to programmatically interact with any +API that exposes a supported schema format. + +## Getting started + +You'll need to install the `coreapi` package using `pip` before you can get +started. + + $ pip install coreapi + +In order to start working with an API, we first need a `Client` instance. The +client holds any configuration around which codecs and transports are supported +when interacting with an API, which allows you to provide for more advanced +kinds of behaviour. + + import coreapi + client = coreapi.Client() + +Once we have a `Client` instance, we can fetch an API schema from the network. + + schema = client.get('https://api.example.org/') + +The object returned from this call will be a `Document` instance, which is +the internal representation of the interface that we are interacting with. + +Now that we have our schema `Document`, we can now start to interact with the API: + + users = client.action(schema, ['users', 'list']) + +Some endpoints may include named parameters, which might be either optional or required: + + new_user = client.action(schema, ['users', 'create'], params={"username": "max"}) + +## Codecs + +Codecs are responsible for encoding or decoding Documents. + +The decoding process is used by a client to take a bytestring of an API schema +definition, and returning the Core API `Document` that represents that interface. + +A codec should be associated with a particular media type, such as `'application/coreapi+json'`. + +This media type is used by the server in the response `Content-Type` header, +in order to indicate what kind of data is being returned in the response. + +#### Configuring codecs + +The codecs that are available can be configured when instantiating a client. +The keyword argument used here is `decoders`, because in the context of a +client the codecs are only for *decoding* responses. + +In the following example we'll configure a client to only accept `Core JSON` +and `JSON` responses. This will allow us to receive and decode a Core JSON schema, +and subsequently to receive JSON responses made against the API. + + from coreapi import codecs, Client + + decoders = [codecs.CoreJSONCodec(), codecs.JSONCodec()] + client = Client(decoders=decoders) + +#### Loading and saving schemas + +You can use a codec directly, in order to load an existing schema definition, +and return the resulting `Document`. + + input_file = open('my-api-schema.json', 'rb') + schema_definition = input_file.read() + codec = codecs.CoreJSONCodec() + schema = codec.load(schema_definition) + +You can also use a codec directly to generate a schema definition given a `Document` instance: + + schema_definition = codec.dump(schema) + output_file = open('my-api-schema.json', 'rb') + output_file.write(schema_definition) + +## Transports + +Transports are responsible for making network requests. The set of transports +that a client has installed determines which network protocols it is able to +support. + +Currently the `coreapi` library only includes an HTTP/HTTPS transport, but +other protocols can also be supported. + +#### Configuring transports + +The behaviour of the network layer can be customized by configuring the +transports that the client is instantiated with. + + import requests + from coreapi import transports, Client + + credentials = {'api.example.org': 'Token 3bd44a009d16ff'} + transports = transports.HTTPTransport(credentials=credentials) + client = Client(transports=transports) + +More complex customizations can also be achieved, for example modifying the +underlying `requests.Session` instance to [attach transport adaptors][transport-adaptors] +that modify the outgoing requests. + +[heroku-api]: https://devcenter.heroku.com/categories/platform-api +[heroku-example]: http://www.coreapi.org/tools-and-resources/example-services/#heroku-json-hyper-schema +[core-api]: http://www.coreapi.org/ +[schema-generation]: ../api-guide/schemas.md +[transport-adaptors]: http://docs.python-requests.org/en/master/user/advanced/#transport-adapters diff --git a/docs/topics/browsable-api.md b/docs/topics/browsable-api.md index 3df95f0b2..bc1431cfc 100644 --- a/docs/topics/browsable-api.md +++ b/docs/topics/browsable-api.md @@ -163,4 +163,4 @@ Better support for autocomplete inputs is planned in future versions. [bcomponentsnav]: http://getbootstrap.com/2.3.2/components.html#navbar [autocomplete-packages]: https://www.djangopackages.com/grids/g/auto-complete/ [django-autocomplete-light]: https://github.com/yourlabs/django-autocomplete-light -[django-autocomplete-light-install]: http://django-autocomplete-light.readthedocs.org/en/latest/#install +[django-autocomplete-light-install]: https://django-autocomplete-light.readthedocs.io/en/master/install.html diff --git a/docs/topics/contributing.md b/docs/topics/contributing.md index c9626ebff..ed7717ae2 100644 --- a/docs/topics/contributing.md +++ b/docs/topics/contributing.md @@ -61,6 +61,7 @@ To run the tests, clone the repository, and then: # Setup the virtual environment virtualenv env source env/bin/activate + pip install django pip install -r requirements.txt # Run the tests @@ -205,7 +206,7 @@ If you want to draw attention to a note or warning, use a pair of enclosing line [pep-8]: http://www.python.org/dev/peps/pep-0008/ [travis-status]: ../img/travis-status.png [pull-requests]: https://help.github.com/articles/using-pull-requests -[tox]: http://tox.readthedocs.org/en/latest/ +[tox]: https://tox.readthedocs.io/en/latest/ [markdown]: http://daringfireball.net/projects/markdown/basics [docs]: https://github.com/tomchristie/django-rest-framework/tree/master/docs [mou]: http://mouapp.com/ diff --git a/docs/topics/documenting-your-api.md b/docs/topics/documenting-your-api.md index e7d2cde0c..ef4d2b4a8 100644 --- a/docs/topics/documenting-your-api.md +++ b/docs/topics/documenting-your-api.md @@ -38,6 +38,34 @@ Both this package and DRF docs are fully documented, well supported, and come hi --- +### DRF AutoDocs + +Oleksander Mashianovs' [DRF Auto Docs][drfautodocs-repo] automated api renderer. + +Collects almost all the code you written into documentation effortlessly. + +Supports: + + * functional view docs + * tree-like structure + * Docstrings: + * markdown + * preserve space & newlines + * formatting with nice syntax + * Fields: + * choices rendering + * help_text (to specify SerializerMethodField output, etc) + * smart read_only/required rendering + * Endpoint properties: + * filter_backends + * authentication_classes + * permission_classes + * extra url params(GET params) + +![whole structure](http://joxi.ru/52aBGNI4k3oyA0.jpg) + +--- + #### Apiary There are various other online tools and services for providing API documentation. One notable service is [Apiary][apiary]. With Apiary, you describe your API using a simple markdown-like syntax. The generated documentation includes API interaction, a mock server for testing & prototyping, and various other tools. @@ -77,7 +105,7 @@ If the python `markdown` library is installed, then [markdown syntax][markdown] [ref]: http://example.com/activating-accounts """ -Note that one constraint of using viewsets is that any documentation be used for all generated views, so for example, you cannot have differing documentation for the generated list view and detail view. +Note that when using viewsets the basic docstring is used for all generated views. To provide descriptions for each view, such as for the the list and retrieve views, use docstring sections as described in [Schemas as documentation: Examples][schemas-examples]. #### The `OPTIONS` method @@ -109,6 +137,7 @@ To implement a hypermedia API you'll need to decide on an appropriate media type [drfdocs-repo]: https://github.com/ekonstantinidis/django-rest-framework-docs [drfdocs-website]: http://www.drfdocs.com/ [drfdocs-demo]: http://demo.drfdocs.com/ +[drfautodocs-repo]: https://github.com/iMakedonsky/drf-autodocs [django-rest-swagger]: https://github.com/marcgibbons/django-rest-swagger [swagger]: https://developers.helloreverb.com/swagger/ [rest-framework-docs]: https://github.com/marcgibbons/django-rest-framework-docs @@ -119,3 +148,4 @@ To implement a hypermedia API you'll need to decide on an appropriate media type [image-django-rest-swagger]: ../img/django-rest-swagger.png [image-apiary]: ../img/apiary.png [image-self-describing-api]: ../img/self-describing.png +[schemas-examples]: api-guide/schemas/#examples diff --git a/docs/topics/funding.md b/docs/topics/funding.md index 7857f8658..91099cb3f 100644 --- a/docs/topics/funding.md +++ b/docs/topics/funding.md @@ -8,6 +8,22 @@ if (window.location.hostname == "www.django-rest-framework.org") { +
+ +
+

Stay up to date, with our monthly progress reports...

+
+ + +
+
+ + +
+ +
+
+ +
+ + -By taking out a paid plan you'll be directly contributing towards making these features happen. +--- + +## Our sponsors + +
+ + diff --git a/docs/topics/html-and-forms.md b/docs/topics/html-and-forms.md index 6660607fe..f77ae3af7 100644 --- a/docs/topics/html-and-forms.md +++ b/docs/topics/html-and-forms.md @@ -108,7 +108,7 @@ Let's take a look at how to render each of the three available template packs. F class LoginSerializer(serializers.Serializer): email = serializers.EmailField( max_length=100, - style={'placeholder': 'Email'} + style={'placeholder': 'Email', 'autofocus': True} ) password = serializers.CharField( max_length=100, @@ -207,9 +207,9 @@ Field templates can also use additional style properties, depending on their typ The complete list of `base_template` options and their associated style options is listed below. -base_template | Valid field types | Additional style options +base_template | Valid field types | Additional style options ----|----|---- -input.html | Any string, numeric or date/time field | input_type, placeholder, hide_label +input.html | Any string, numeric or date/time field | input_type, placeholder, hide_label, autofocus textarea.html | `CharField` | rows, placeholder, hide_label select.html | `ChoiceField` or relational field types | hide_label radio.html | `ChoiceField` or relational field types | inline, hide_label diff --git a/docs/topics/kickstarter-announcement.md b/docs/topics/kickstarter-announcement.md index 78c5cce6f..6b7b428d1 100644 --- a/docs/topics/kickstarter-announcement.md +++ b/docs/topics/kickstarter-announcement.md @@ -102,7 +102,7 @@ Our gold sponsors include companies large and small. Many thanks for their signi ### Silver sponsors -The serious financial contribution that our silver sponsors have made is very much appreciated. I'd like to say a particular thank you to individuals who have choosen to privately support the project at this level. +The serious financial contribution that our silver sponsors have made is very much appreciated. I'd like to say a particular thank you to individuals who have chosen to privately support the project at this level.