diff --git a/api-guide/fields/index.html b/api-guide/fields/index.html
index e4857b6d8..5577de0a5 100644
--- a/api-guide/fields/index.html
+++ b/api-guide/fields/index.html
@@ -831,10 +831,11 @@ explicitly declare the BooleanField
on the serializer class, or use
A date and time representation.
Corresponds to django.db.models.fields.DateTimeField
.
-Signature: DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
+Signature: DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None, default_timezone=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.
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']
.
+default_timezone
- A pytz.timezone
representing the timezone. If not specified and the USE_TZ
setting is enabled, this defaults to the current timezone. If USE_TZ
is disabled, then datetime objects will be naive.
Format strings may either be Python strftime formats which explicitly specify the format, or the special string 'iso-8601'
, which indicates that ISO 8601 style datetimes should be used. (eg '2013-01-29T12:34:56.000000Z'
)
diff --git a/api-guide/permissions/index.html b/api-guide/permissions/index.html
index 99913c616..57b0024dd 100644
--- a/api-guide/permissions/index.html
+++ b/api-guide/permissions/index.html
@@ -547,7 +547,7 @@ or if you override the get_object
method on a generic view, then yo
Note: With the exception of DjangoObjectPermissions
, the provided
-permission classes in rest_framework.permssions
do not implement the
+permission classes in rest_framework.permissions
do not implement the
methods necessary to check object permissions.
If you wish to use the provided permission classes in order to check object
permissions, you must subclass them and implement the
diff --git a/api-guide/schemas/index.html b/api-guide/schemas/index.html
index 87f266358..5e6a89815 100644
--- a/api-guide/schemas/index.html
+++ b/api-guide/schemas/index.html
@@ -539,7 +539,7 @@ can render the schema into the commonly used YAML-based OpenAPI format.
pip install coreapi pyyaml
-There are two different ways you can serve a schema description for you API.
+There are two different ways you can serve a schema description for your API.
To generate a static API schema, use the generateschema
management command.
$ python manage.py generateschema > schema.yml
diff --git a/api-guide/serializers/index.html b/api-guide/serializers/index.html
index 6c2df2e80..580625186 100644
--- a/api-guide/serializers/index.html
+++ b/api-guide/serializers/index.html
@@ -1085,6 +1085,7 @@ AccountSerializer():
user.save()
return user
+Please keep in mind that, if the field has already been explicitly declared on the serializer class, then the extra_kwargs
option will be ignored.
When serializing model instances, there are a number of different ways you might choose to represent relationships. The default representation for ModelSerializer
is to use the primary keys of the related instances.
Alternative representations include serializing using hyperlinks, serializing complete nested representations, or serializing with a custom representation.
@@ -1111,7 +1112,7 @@ AccountSerializer():
The default implementation returns a serializer class based on the serializer_field_mapping
attribute.
Called to generate a serializer field that maps to a relational model field.
-The default implementation returns a serializer class based on the serializer_relational_field
attribute.
+The default implementation returns a serializer class based on the serializer_related_field
attribute.
The relation_info
argument is a named tuple, that contains model_field
, related_model
, to_many
and has_through_model
properties.
Called to generate a serializer field that maps to a relational model field, when the depth
option has been set.
@@ -1375,7 +1376,7 @@ def all_high_scores(request):
def to_representation(self, obj):
for attribute_name in dir(obj):
attribute = getattr(obj, attribute_name)
- if attribute_name('_'):
+ if attribute_name.startswith('_'):
# Ignore private attributes.
pass
elif hasattr(attribute, '__call__'):
diff --git a/community/3.0-announcement/index.html b/community/3.0-announcement/index.html
index 91e17b1e2..b3d00acd1 100644
--- a/community/3.0-announcement/index.html
+++ b/community/3.0-announcement/index.html
@@ -875,7 +875,7 @@ def all_high_scores(request):
def to_representation(self, obj):
for attribute_name in dir(obj):
attribute = getattr(obj, attribute_name)
- if attribute_name('_'):
+ if attribute_name.startswith('_'):
# Ignore private attributes.
pass
elif hasattr(attribute, '__call__'):
diff --git a/community/release-notes/index.html b/community/release-notes/index.html
index e3ddd77c2..dcc29a434 100644
--- a/community/release-notes/index.html
+++ b/community/release-notes/index.html
@@ -505,6 +505,14 @@
+
+Date: [29th April 2019]
+This is the last Django REST Framework release that will support Python 2.
+Be sure to upgrade to Python 3 before upgrading to Django REST Framework 3.10.
+
+- Adjusted the compat check for django-guardian to allow the last guardian
+ version (v1.4.9) compatible with Python 2. #6613
+
Date: 3rd March 2019
@@ -525,7 +533,7 @@
- Introduced
RemovedInDRF…Warning
classes to simplify deprecations. #6480
-Date: 16th Janurary 2019
+Date: 16th January 2019
- Resolve XSS issue in browsable API. #6330
- Upgrade Bootstrap to 3.4.0 to resolve XSS issue.
@@ -1578,6 +1586,8 @@ Previously may have been stored internally as None
.
+
+
diff --git a/community/third-party-packages/index.html b/community/third-party-packages/index.html
index f990539da..bca120561 100644
--- a/community/third-party-packages/index.html
+++ b/community/third-party-packages/index.html
@@ -633,6 +633,7 @@ You probably want to also tag the version now:
- django-rest-messaging, django-rest-messaging-centrifugo and django-rest-messaging-js - A real-time pluggable messaging service using DRM.
- djangorest-alchemy - SQLAlchemy support for REST framework.
- djangorestframework-datatables - Seamless integration between Django REST framework and Datatables.
+- django-rest-framework-condition - Decorators for managing HTTP cache headers for Django REST framework (ETag and Last-modified).
diff --git a/mkdocs/search_index.json b/mkdocs/search_index.json
index 55cc65e28..d365c675f 100644
--- a/mkdocs/search_index.json
+++ b/mkdocs/search_index.json
@@ -1,6178 +1,6183 @@
{
"docs": [
{
- "location": "/",
- "text": ".promo li a {\n float: left;\n width: 130px;\n height: 20px;\n text-align: center;\n margin: 10px 30px;\n padding: 150px 0 0 0;\n background-position: 0 50%;\n background-size: 130px auto;\n background-repeat: no-repeat;\n font-size: 120%;\n color: black;\n}\n.promo li {\n list-style: none;\n}\n\n\n\n\n\n \n\n\n \n\n \n\n \n\n\n \n\n \n\n \n\n\n\n\n\n\n\n\n\nDjango REST Framework\n\n\n\n\n\n\n\n\nDjango REST framework is a powerful and flexible toolkit for building Web APIs.\n\n\nSome reasons you might want to use REST framework:\n\n\n\n\nThe \nWeb browsable API\n is a huge usability win for your developers.\n\n\nAuthentication policies\n including packages for \nOAuth1a\n and \nOAuth2\n.\n\n\nSerialization\n that supports both \nORM\n and \nnon-ORM\n data sources.\n\n\nCustomizable all the way down - just use \nregular function-based views\n if you don't need the \nmore\n \npowerful\n \nfeatures\n.\n\n\nExtensive documentation\n, and \ngreat community support\n.\n\n\nUsed and trusted by internationally recognised companies including \nMozilla\n, \nRed Hat\n, \nHeroku\n, and \nEventbrite\n.\n\n\n\n\n\n\nFunding\n\n\nREST framework is a \ncollaboratively funded project\n. If you use\nREST framework commercially we strongly encourage you to invest in its\ncontinued development by \nsigning up for a paid plan\n.\n\n\nEvery single sign-up helps us make REST framework long-term financially sustainable.\n\n\n\n \nSentry\n\n \nStream\n\n \nRelease History\n\n \nRollbar\n\n \nCadre\n\n \nKloudless\n\n \nLights On Software\n\n\n\n\n\n\n\n\nMany thanks to all our \nwonderful sponsors\n, and in particular to our premium backers, \nSentry\n, \nStream\n, \nRelease History\n, \nRollbar\n, \nCadre\n, \nKloudless\n, and \nLights On Software\n.\n\n\n\n\nRequirements\n\n\nREST framework requires the following:\n\n\n\n\nPython (2.7, 3.4, 3.5, 3.6, 3.7)\n\n\nDjango (1.11, 2.0, 2.1, 2.2)\n\n\n\n\nWe \nhighly recommend\n and only officially support the latest patch release of\neach Python and Django series.\n\n\nThe following packages are optional:\n\n\n\n\ncoreapi\n (1.32.0+) - Schema generation support.\n\n\nMarkdown\n (2.1.0+) - Markdown support for the browsable API.\n\n\ndjango-filter\n (1.0.1+) - Filtering support.\n\n\ndjango-crispy-forms\n - Improved HTML display for filtering.\n\n\ndjango-guardian\n (1.1.1+) - Object level permissions support.\n\n\n\n\nInstallation\n\n\nInstall using \npip\n, including any optional packages you want...\n\n\npip install djangorestframework\npip install markdown # Markdown support for the browsable API.\npip install django-filter # Filtering support\n\n\n\n...or clone the project from github.\n\n\ngit clone https://github.com/encode/django-rest-framework\n\n\n\nAdd \n'rest_framework'\n to your \nINSTALLED_APPS\n setting.\n\n\nINSTALLED_APPS = (\n ...\n 'rest_framework',\n)\n\n\n\nIf you're intending to use the browsable API you'll probably also want to add REST framework's login and logout views. Add the following to your root \nurls.py\n file.\n\n\nurlpatterns = [\n ...\n url(r'^api-auth/', include('rest_framework.urls'))\n]\n\n\n\nNote that the URL path can be whatever you want.\n\n\nExample\n\n\nLet's take a look at a quick example of using REST framework to build a simple model-backed API.\n\n\nWe'll create a read-write API for accessing information on the users of our project.\n\n\nAny global settings for a REST framework API are kept in a single configuration dictionary named \nREST_FRAMEWORK\n. Start off by adding the following to your \nsettings.py\n module:\n\n\nREST_FRAMEWORK = {\n # Use Django's standard `django.contrib.auth` permissions,\n # or allow read-only access for unauthenticated users.\n 'DEFAULT_PERMISSION_CLASSES': [\n 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'\n ]\n}\n\n\n\nDon't forget to make sure you've also added \nrest_framework\n to your \nINSTALLED_APPS\n.\n\n\nWe're ready to create our API now.\nHere's our project's root \nurls.py\n module:\n\n\nfrom django.conf.urls import url, include\nfrom django.contrib.auth.models import User\nfrom rest_framework import routers, serializers, viewsets\n\n# Serializers define the API representation.\nclass UserSerializer(serializers.HyperlinkedModelSerializer):\n class Meta:\n model = User\n fields = ('url', 'username', 'email', 'is_staff')\n\n# ViewSets define the view behavior.\nclass UserViewSet(viewsets.ModelViewSet):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n# Routers provide an easy way of automatically determining the URL conf.\nrouter = routers.DefaultRouter()\nrouter.register(r'users', UserViewSet)\n\n# Wire up our API using automatic URL routing.\n# Additionally, we include login URLs for the browsable API.\nurlpatterns = [\n url(r'^', include(router.urls)),\n url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))\n]\n\n\n\nYou can now open the API in your browser at \nhttp://127.0.0.1:8000/\n, and view your new 'users' API. If you use the login control in the top right corner you'll also be able to add, create and delete users from the system.\n\n\nQuickstart\n\n\nCan't wait to get started? The \nquickstart guide\n is the fastest way to get up and running, and building APIs with REST framework.\n\n\nDevelopment\n\n\nSee the \nContribution guidelines\n for information on how to clone\nthe repository, run the test suite and contribute changes back to REST\nFramework.\n\n\nSupport\n\n\nFor support please see the \nREST framework discussion group\n, try the \n#restframework\n channel on \nirc.freenode.net\n, search \nthe IRC archives\n, or raise a question on \nStack Overflow\n, making sure to include the \n'django-rest-framework'\n tag.\n\n\nFor priority support please sign up for a \nprofessional or premium sponsorship plan\n.\n\n\nFor updates on REST framework development, you may also want to follow \nthe author\n on Twitter.\n\n\nFollow @_tomchristie\n\n\n!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=\"//platform.twitter.com/widgets.js\";fjs.parentNode.insertBefore(js,fjs);}}(document,\"script\",\"twitter-wjs\");\n\n\nSecurity\n\n\nIf you believe you\u2019ve found something in Django REST framework which has security implications, please \ndo not raise the issue in a public forum\n.\n\n\nSend a description of the issue via email to \nrest-framework-security@googlegroups.com\n. The project maintainers will then work with you to resolve any issues where required, prior to any public disclosure.\n\n\nLicense\n\n\nCopyright \u00a9 2011-present, \nEncode OSS Ltd\n.\nAll rights reserved.\n\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n\n\n\n\n\nRedistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n\n\n\n\n\nRedistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n\n\n\n\n\nNeither the name of the copyright holder nor the names of its\n contributors may be used to endorse or promote products derived from\n this software without specific prior written permission.\n\n\n\n\n\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
+ "location": "/",
+ "text": ".promo li a {\n float: left;\n width: 130px;\n height: 20px;\n text-align: center;\n margin: 10px 30px;\n padding: 150px 0 0 0;\n background-position: 0 50%;\n background-size: 130px auto;\n background-repeat: no-repeat;\n font-size: 120%;\n color: black;\n}\n.promo li {\n list-style: none;\n}\n\n\n\n\n\n \n\n\n \n\n \n\n \n\n\n \n\n \n\n \n\n\n\n\n\n\n\n\n\nDjango REST Framework\n\n\n\n\n\n\n\n\nDjango REST framework is a powerful and flexible toolkit for building Web APIs.\n\n\nSome reasons you might want to use REST framework:\n\n\n\n\nThe \nWeb browsable API\n is a huge usability win for your developers.\n\n\nAuthentication policies\n including packages for \nOAuth1a\n and \nOAuth2\n.\n\n\nSerialization\n that supports both \nORM\n and \nnon-ORM\n data sources.\n\n\nCustomizable all the way down - just use \nregular function-based views\n if you don't need the \nmore\n \npowerful\n \nfeatures\n.\n\n\nExtensive documentation\n, and \ngreat community support\n.\n\n\nUsed and trusted by internationally recognised companies including \nMozilla\n, \nRed Hat\n, \nHeroku\n, and \nEventbrite\n.\n\n\n\n\n\n\nFunding\n\n\nREST framework is a \ncollaboratively funded project\n. If you use\nREST framework commercially we strongly encourage you to invest in its\ncontinued development by \nsigning up for a paid plan\n.\n\n\nEvery single sign-up helps us make REST framework long-term financially sustainable.\n\n\n\n \nSentry\n\n \nStream\n\n \nRelease History\n\n \nRollbar\n\n \nCadre\n\n \nKloudless\n\n \nLights On Software\n\n\n\n\n\n\n\n\nMany thanks to all our \nwonderful sponsors\n, and in particular to our premium backers, \nSentry\n, \nStream\n, \nRelease History\n, \nRollbar\n, \nCadre\n, \nKloudless\n, and \nLights On Software\n.\n\n\n\n\nRequirements\n\n\nREST framework requires the following:\n\n\n\n\nPython (2.7, 3.4, 3.5, 3.6, 3.7)\n\n\nDjango (1.11, 2.0, 2.1, 2.2)\n\n\n\n\nWe \nhighly recommend\n and only officially support the latest patch release of\neach Python and Django series.\n\n\nThe following packages are optional:\n\n\n\n\ncoreapi\n (1.32.0+) - Schema generation support.\n\n\nMarkdown\n (2.1.0+) - Markdown support for the browsable API.\n\n\ndjango-filter\n (1.0.1+) - Filtering support.\n\n\ndjango-crispy-forms\n - Improved HTML display for filtering.\n\n\ndjango-guardian\n (1.1.1+) - Object level permissions support.\n\n\n\n\nInstallation\n\n\nInstall using \npip\n, including any optional packages you want...\n\n\npip install djangorestframework\npip install markdown # Markdown support for the browsable API.\npip install django-filter # Filtering support\n\n\n\n...or clone the project from github.\n\n\ngit clone https://github.com/encode/django-rest-framework\n\n\n\nAdd \n'rest_framework'\n to your \nINSTALLED_APPS\n setting.\n\n\nINSTALLED_APPS = (\n ...\n 'rest_framework',\n)\n\n\n\nIf you're intending to use the browsable API you'll probably also want to add REST framework's login and logout views. Add the following to your root \nurls.py\n file.\n\n\nurlpatterns = [\n ...\n url(r'^api-auth/', include('rest_framework.urls'))\n]\n\n\n\nNote that the URL path can be whatever you want.\n\n\nExample\n\n\nLet's take a look at a quick example of using REST framework to build a simple model-backed API.\n\n\nWe'll create a read-write API for accessing information on the users of our project.\n\n\nAny global settings for a REST framework API are kept in a single configuration dictionary named \nREST_FRAMEWORK\n. Start off by adding the following to your \nsettings.py\n module:\n\n\nREST_FRAMEWORK = {\n # Use Django's standard `django.contrib.auth` permissions,\n # or allow read-only access for unauthenticated users.\n 'DEFAULT_PERMISSION_CLASSES': [\n 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'\n ]\n}\n\n\n\nDon't forget to make sure you've also added \nrest_framework\n to your \nINSTALLED_APPS\n.\n\n\nWe're ready to create our API now.\nHere's our project's root \nurls.py\n module:\n\n\nfrom django.conf.urls import url, include\nfrom django.contrib.auth.models import User\nfrom rest_framework import routers, serializers, viewsets\n\n# Serializers define the API representation.\nclass UserSerializer(serializers.HyperlinkedModelSerializer):\n class Meta:\n model = User\n fields = ('url', 'username', 'email', 'is_staff')\n\n# ViewSets define the view behavior.\nclass UserViewSet(viewsets.ModelViewSet):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n# Routers provide an easy way of automatically determining the URL conf.\nrouter = routers.DefaultRouter()\nrouter.register(r'users', UserViewSet)\n\n# Wire up our API using automatic URL routing.\n# Additionally, we include login URLs for the browsable API.\nurlpatterns = [\n url(r'^', include(router.urls)),\n url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))\n]\n\n\n\nYou can now open the API in your browser at \nhttp://127.0.0.1:8000/\n, and view your new 'users' API. If you use the login control in the top right corner you'll also be able to add, create and delete users from the system.\n\n\nQuickstart\n\n\nCan't wait to get started? The \nquickstart guide\n is the fastest way to get up and running, and building APIs with REST framework.\n\n\nDevelopment\n\n\nSee the \nContribution guidelines\n for information on how to clone\nthe repository, run the test suite and contribute changes back to REST\nFramework.\n\n\nSupport\n\n\nFor support please see the \nREST framework discussion group\n, try the \n#restframework\n channel on \nirc.freenode.net\n, search \nthe IRC archives\n, or raise a question on \nStack Overflow\n, making sure to include the \n'django-rest-framework'\n tag.\n\n\nFor priority support please sign up for a \nprofessional or premium sponsorship plan\n.\n\n\nFor updates on REST framework development, you may also want to follow \nthe author\n on Twitter.\n\n\nFollow @_tomchristie\n\n\n!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=\"//platform.twitter.com/widgets.js\";fjs.parentNode.insertBefore(js,fjs);}}(document,\"script\",\"twitter-wjs\");\n\n\nSecurity\n\n\nIf you believe you\u2019ve found something in Django REST framework which has security implications, please \ndo not raise the issue in a public forum\n.\n\n\nSend a description of the issue via email to \nrest-framework-security@googlegroups.com\n. The project maintainers will then work with you to resolve any issues where required, prior to any public disclosure.\n\n\nLicense\n\n\nCopyright \u00a9 2011-present, \nEncode OSS Ltd\n.\nAll rights reserved.\n\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n\n\n\n\n\nRedistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n\n\n\n\n\nRedistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n\n\n\n\n\nNeither the name of the copyright holder nor the names of its\n contributors may be used to endorse or promote products derived from\n this software without specific prior written permission.\n\n\n\n\n\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
"title": "Home"
- },
+ },
{
- "location": "/#funding",
- "text": "REST framework is a collaboratively funded project . If you use\nREST framework commercially we strongly encourage you to invest in its\ncontinued development by signing up for a paid plan . Every single sign-up helps us make REST framework long-term financially sustainable. \n Sentry \n Stream \n Release History \n Rollbar \n Cadre \n Kloudless \n Lights On Software Many thanks to all our wonderful sponsors , and in particular to our premium backers, Sentry , Stream , Release History , Rollbar , Cadre , Kloudless , and Lights On Software .",
+ "location": "/#funding",
+ "text": "REST framework is a collaboratively funded project . If you use\nREST framework commercially we strongly encourage you to invest in its\ncontinued development by signing up for a paid plan . Every single sign-up helps us make REST framework long-term financially sustainable. \n Sentry \n Stream \n Release History \n Rollbar \n Cadre \n Kloudless \n Lights On Software Many thanks to all our wonderful sponsors , and in particular to our premium backers, Sentry , Stream , Release History , Rollbar , Cadre , Kloudless , and Lights On Software .",
"title": "Funding"
- },
+ },
{
- "location": "/#requirements",
- "text": "REST framework requires the following: Python (2.7, 3.4, 3.5, 3.6, 3.7) Django (1.11, 2.0, 2.1, 2.2) We highly recommend and only officially support the latest patch release of\neach Python and Django series. The following packages are optional: coreapi (1.32.0+) - Schema generation support. Markdown (2.1.0+) - Markdown support for the browsable API. django-filter (1.0.1+) - Filtering support. django-crispy-forms - Improved HTML display for filtering. django-guardian (1.1.1+) - Object level permissions support.",
+ "location": "/#requirements",
+ "text": "REST framework requires the following: Python (2.7, 3.4, 3.5, 3.6, 3.7) Django (1.11, 2.0, 2.1, 2.2) We highly recommend and only officially support the latest patch release of\neach Python and Django series. The following packages are optional: coreapi (1.32.0+) - Schema generation support. Markdown (2.1.0+) - Markdown support for the browsable API. django-filter (1.0.1+) - Filtering support. django-crispy-forms - Improved HTML display for filtering. django-guardian (1.1.1+) - Object level permissions support.",
"title": "Requirements"
- },
+ },
{
- "location": "/#installation",
- "text": "Install using pip , including any optional packages you want... pip install djangorestframework\npip install markdown # Markdown support for the browsable API.\npip install django-filter # Filtering support ...or clone the project from github. git clone https://github.com/encode/django-rest-framework Add 'rest_framework' to your INSTALLED_APPS setting. INSTALLED_APPS = (\n ...\n 'rest_framework',\n) If you're intending to use the browsable API you'll probably also want to add REST framework's login and logout views. Add the following to your root urls.py file. urlpatterns = [\n ...\n url(r'^api-auth/', include('rest_framework.urls'))\n] Note that the URL path can be whatever you want.",
+ "location": "/#installation",
+ "text": "Install using pip , including any optional packages you want... pip install djangorestframework\npip install markdown # Markdown support for the browsable API.\npip install django-filter # Filtering support ...or clone the project from github. git clone https://github.com/encode/django-rest-framework Add 'rest_framework' to your INSTALLED_APPS setting. INSTALLED_APPS = (\n ...\n 'rest_framework',\n) If you're intending to use the browsable API you'll probably also want to add REST framework's login and logout views. Add the following to your root urls.py file. urlpatterns = [\n ...\n url(r'^api-auth/', include('rest_framework.urls'))\n] Note that the URL path can be whatever you want.",
"title": "Installation"
- },
+ },
{
- "location": "/#example",
- "text": "Let's take a look at a quick example of using REST framework to build a simple model-backed API. We'll create a read-write API for accessing information on the users of our project. Any global settings for a REST framework API are kept in a single configuration dictionary named REST_FRAMEWORK . Start off by adding the following to your settings.py module: REST_FRAMEWORK = {\n # Use Django's standard `django.contrib.auth` permissions,\n # or allow read-only access for unauthenticated users.\n 'DEFAULT_PERMISSION_CLASSES': [\n 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'\n ]\n} Don't forget to make sure you've also added rest_framework to your INSTALLED_APPS . We're ready to create our API now.\nHere's our project's root urls.py module: from django.conf.urls import url, include\nfrom django.contrib.auth.models import User\nfrom rest_framework import routers, serializers, viewsets\n\n# Serializers define the API representation.\nclass UserSerializer(serializers.HyperlinkedModelSerializer):\n class Meta:\n model = User\n fields = ('url', 'username', 'email', 'is_staff')\n\n# ViewSets define the view behavior.\nclass UserViewSet(viewsets.ModelViewSet):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n# Routers provide an easy way of automatically determining the URL conf.\nrouter = routers.DefaultRouter()\nrouter.register(r'users', UserViewSet)\n\n# Wire up our API using automatic URL routing.\n# Additionally, we include login URLs for the browsable API.\nurlpatterns = [\n url(r'^', include(router.urls)),\n url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))\n] You can now open the API in your browser at http://127.0.0.1:8000/ , and view your new 'users' API. If you use the login control in the top right corner you'll also be able to add, create and delete users from the system.",
+ "location": "/#example",
+ "text": "Let's take a look at a quick example of using REST framework to build a simple model-backed API. We'll create a read-write API for accessing information on the users of our project. Any global settings for a REST framework API are kept in a single configuration dictionary named REST_FRAMEWORK . Start off by adding the following to your settings.py module: REST_FRAMEWORK = {\n # Use Django's standard `django.contrib.auth` permissions,\n # or allow read-only access for unauthenticated users.\n 'DEFAULT_PERMISSION_CLASSES': [\n 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'\n ]\n} Don't forget to make sure you've also added rest_framework to your INSTALLED_APPS . We're ready to create our API now.\nHere's our project's root urls.py module: from django.conf.urls import url, include\nfrom django.contrib.auth.models import User\nfrom rest_framework import routers, serializers, viewsets\n\n# Serializers define the API representation.\nclass UserSerializer(serializers.HyperlinkedModelSerializer):\n class Meta:\n model = User\n fields = ('url', 'username', 'email', 'is_staff')\n\n# ViewSets define the view behavior.\nclass UserViewSet(viewsets.ModelViewSet):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n# Routers provide an easy way of automatically determining the URL conf.\nrouter = routers.DefaultRouter()\nrouter.register(r'users', UserViewSet)\n\n# Wire up our API using automatic URL routing.\n# Additionally, we include login URLs for the browsable API.\nurlpatterns = [\n url(r'^', include(router.urls)),\n url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))\n] You can now open the API in your browser at http://127.0.0.1:8000/ , and view your new 'users' API. If you use the login control in the top right corner you'll also be able to add, create and delete users from the system.",
"title": "Example"
- },
+ },
{
- "location": "/#quickstart",
- "text": "Can't wait to get started? The quickstart guide is the fastest way to get up and running, and building APIs with REST framework.",
+ "location": "/#quickstart",
+ "text": "Can't wait to get started? The quickstart guide is the fastest way to get up and running, and building APIs with REST framework.",
"title": "Quickstart"
- },
+ },
{
- "location": "/#development",
- "text": "See the Contribution guidelines for information on how to clone\nthe repository, run the test suite and contribute changes back to REST\nFramework.",
+ "location": "/#development",
+ "text": "See the Contribution guidelines for information on how to clone\nthe repository, run the test suite and contribute changes back to REST\nFramework.",
"title": "Development"
- },
+ },
{
- "location": "/#support",
- "text": "For support please see the REST framework discussion group , try the #restframework channel on irc.freenode.net , search the IRC archives , or raise a question on Stack Overflow , making sure to include the 'django-rest-framework' tag. For priority support please sign up for a professional or premium sponsorship plan . For updates on REST framework development, you may also want to follow the author on Twitter. Follow @_tomchristie !function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=\"//platform.twitter.com/widgets.js\";fjs.parentNode.insertBefore(js,fjs);}}(document,\"script\",\"twitter-wjs\");",
+ "location": "/#support",
+ "text": "For support please see the REST framework discussion group , try the #restframework channel on irc.freenode.net , search the IRC archives , or raise a question on Stack Overflow , making sure to include the 'django-rest-framework' tag. For priority support please sign up for a professional or premium sponsorship plan . For updates on REST framework development, you may also want to follow the author on Twitter. Follow @_tomchristie !function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=\"//platform.twitter.com/widgets.js\";fjs.parentNode.insertBefore(js,fjs);}}(document,\"script\",\"twitter-wjs\");",
"title": "Support"
- },
+ },
{
- "location": "/#security",
- "text": "If you believe you\u2019ve 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 . The project maintainers will then work with you to resolve any issues where required, prior to any public disclosure.",
+ "location": "/#security",
+ "text": "If you believe you\u2019ve 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 . The project maintainers will then work with you to resolve any issues where required, prior to any public disclosure.",
"title": "Security"
- },
+ },
{
- "location": "/#license",
- "text": "Copyright \u00a9 2011-present, Encode OSS Ltd .\nAll rights reserved. Redistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution. Neither the name of the copyright holder nor the names of its\n contributors may be used to endorse or promote products derived from\n this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
+ "location": "/#license",
+ "text": "Copyright \u00a9 2011-present, Encode OSS Ltd .\nAll rights reserved. Redistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution. Neither the name of the copyright holder nor the names of its\n contributors may be used to endorse or promote products derived from\n this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
"title": "License"
- },
+ },
{
- "location": "/tutorial/quickstart/",
- "text": "Quickstart\n\n\nWe're going to create a simple API to allow admin users to view and edit the users and groups in the system.\n\n\nProject setup\n\n\nCreate a new Django project named \ntutorial\n, then start a new app called \nquickstart\n.\n\n\n# Create the project directory\nmkdir tutorial\ncd tutorial\n\n# Create a virtualenv to isolate our package dependencies locally\nvirtualenv env\nsource env/bin/activate # On Windows use `env\\Scripts\\activate`\n\n# Install Django and Django REST framework into the virtualenv\npip install django\npip install djangorestframework\n\n# Set up a new project with a single application\ndjango-admin startproject tutorial . # Note the trailing '.' character\ncd tutorial\ndjango-admin startapp quickstart\ncd ..\n\n\n\nThe project layout should look like:\n\n\n$ pwd\n/tutorial\n$ find .\n.\n./manage.py\n./tutorial\n./tutorial/__init__.py\n./tutorial/quickstart\n./tutorial/quickstart/__init__.py\n./tutorial/quickstart/admin.py\n./tutorial/quickstart/apps.py\n./tutorial/quickstart/migrations\n./tutorial/quickstart/migrations/__init__.py\n./tutorial/quickstart/models.py\n./tutorial/quickstart/tests.py\n./tutorial/quickstart/views.py\n./tutorial/settings.py\n./tutorial/urls.py\n./tutorial/wsgi.py\n\n\n\nIt may look unusual that the application has been created within the project directory. Using the project's namespace avoids name clashes with external modules (a topic that goes outside the scope of the quickstart).\n\n\nNow sync your database for the first time:\n\n\npython manage.py migrate\n\n\n\nWe'll also create an initial user named \nadmin\n with a password of \npassword123\n. We'll authenticate as that user later in our example.\n\n\npython manage.py createsuperuser --email admin@example.com --username admin\n\n\n\nOnce you've set up a database and the initial user is created and ready to go, open up the app's directory and we'll get coding...\n\n\nSerializers\n\n\nFirst up we're going to define some serializers. Let's create a new module named \ntutorial/quickstart/serializers.py\n that we'll use for our data representations.\n\n\nfrom django.contrib.auth.models import User, Group\nfrom rest_framework import serializers\n\n\nclass UserSerializer(serializers.HyperlinkedModelSerializer):\n class Meta:\n model = User\n fields = ('url', 'username', 'email', 'groups')\n\n\nclass GroupSerializer(serializers.HyperlinkedModelSerializer):\n class Meta:\n model = Group\n fields = ('url', 'name')\n\n\n\nNotice that we're using hyperlinked relations in this case with \nHyperlinkedModelSerializer\n. You can also use primary key and various other relationships, but hyperlinking is good RESTful design.\n\n\nViews\n\n\nRight, we'd better write some views then. Open \ntutorial/quickstart/views.py\n and get typing.\n\n\nfrom django.contrib.auth.models import User, Group\nfrom rest_framework import viewsets\nfrom tutorial.quickstart.serializers import UserSerializer, GroupSerializer\n\n\nclass UserViewSet(viewsets.ModelViewSet):\n \"\"\"\n API endpoint that allows users to be viewed or edited.\n \"\"\"\n queryset = User.objects.all().order_by('-date_joined')\n serializer_class = UserSerializer\n\n\nclass GroupViewSet(viewsets.ModelViewSet):\n \"\"\"\n API endpoint that allows groups to be viewed or edited.\n \"\"\"\n queryset = Group.objects.all()\n serializer_class = GroupSerializer\n\n\n\nRather than write multiple views we're grouping together all the common behavior into classes called \nViewSets\n.\n\n\nWe can easily break these down into individual views if we need to, but using viewsets keeps the view logic nicely organized as well as being very concise.\n\n\nURLs\n\n\nOkay, now let's wire up the API URLs. On to \ntutorial/urls.py\n...\n\n\nfrom django.urls import include, path\nfrom rest_framework import routers\nfrom tutorial.quickstart import views\n\nrouter = routers.DefaultRouter()\nrouter.register(r'users', views.UserViewSet)\nrouter.register(r'groups', views.GroupViewSet)\n\n# Wire up our API using automatic URL routing.\n# Additionally, we include login URLs for the browsable API.\nurlpatterns = [\n path('', include(router.urls)),\n path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))\n]\n\n\n\nBecause we're using viewsets instead of views, we can automatically generate the URL conf for our API, by simply registering the viewsets with a router class.\n\n\nAgain, if we need more control over the API URLs we can simply drop down to using regular class-based views, and writing the URL conf explicitly.\n\n\nFinally, we're including default login and logout views for use with the browsable API. That's optional, but useful if your API requires authentication and you want to use the browsable API.\n\n\nPagination\n\n\nPagination allows you to control how many objects per page are returned. To enable it add the following lines to \ntutorial/settings.py\n\n\nREST_FRAMEWORK = {\n 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',\n 'PAGE_SIZE': 10\n}\n\n\n\nSettings\n\n\nAdd \n'rest_framework'\n to \nINSTALLED_APPS\n. The settings module will be in \ntutorial/settings.py\n\n\nINSTALLED_APPS = (\n ...\n 'rest_framework',\n)\n\n\n\nOkay, we're done.\n\n\n\n\nTesting our API\n\n\nWe're now ready to test the API we've built. Let's fire up the server from the command line.\n\n\npython manage.py runserver\n\n\n\nWe can now access our API, both from the command-line, using tools like \ncurl\n...\n\n\nbash: curl -H 'Accept: application/json; indent=4' -u admin:password123 http://127.0.0.1:8000/users/\n{\n \"count\": 2,\n \"next\": null,\n \"previous\": null,\n \"results\": [\n {\n \"email\": \"admin@example.com\",\n \"groups\": [],\n \"url\": \"http://127.0.0.1:8000/users/1/\",\n \"username\": \"admin\"\n },\n {\n \"email\": \"tom@example.com\",\n \"groups\": [ ],\n \"url\": \"http://127.0.0.1:8000/users/2/\",\n \"username\": \"tom\"\n }\n ]\n}\n\n\n\nOr using the \nhttpie\n, command line tool...\n\n\nbash: http -a admin:password123 http://127.0.0.1:8000/users/\n\nHTTP/1.1 200 OK\n...\n{\n \"count\": 2,\n \"next\": null,\n \"previous\": null,\n \"results\": [\n {\n \"email\": \"admin@example.com\",\n \"groups\": [],\n \"url\": \"http://localhost:8000/users/1/\",\n \"username\": \"paul\"\n },\n {\n \"email\": \"tom@example.com\",\n \"groups\": [ ],\n \"url\": \"http://127.0.0.1:8000/users/2/\",\n \"username\": \"tom\"\n }\n ]\n}\n\n\n\nOr directly through the browser, by going to the URL \nhttp://127.0.0.1:8000/users/\n...\n\n\n\n\nIf you're working through the browser, make sure to login using the control in the top right corner.\n\n\nGreat, that was easy!\n\n\nIf you want to get a more in depth understanding of how REST framework fits together head on over to \nthe tutorial\n, or start browsing the \nAPI guide\n.",
+ "location": "/tutorial/quickstart/",
+ "text": "Quickstart\n\n\nWe're going to create a simple API to allow admin users to view and edit the users and groups in the system.\n\n\nProject setup\n\n\nCreate a new Django project named \ntutorial\n, then start a new app called \nquickstart\n.\n\n\n# Create the project directory\nmkdir tutorial\ncd tutorial\n\n# Create a virtualenv to isolate our package dependencies locally\nvirtualenv env\nsource env/bin/activate # On Windows use `env\\Scripts\\activate`\n\n# Install Django and Django REST framework into the virtualenv\npip install django\npip install djangorestframework\n\n# Set up a new project with a single application\ndjango-admin startproject tutorial . # Note the trailing '.' character\ncd tutorial\ndjango-admin startapp quickstart\ncd ..\n\n\n\nThe project layout should look like:\n\n\n$ pwd\n\nsome path\n/tutorial\n$ find .\n.\n./manage.py\n./tutorial\n./tutorial/__init__.py\n./tutorial/quickstart\n./tutorial/quickstart/__init__.py\n./tutorial/quickstart/admin.py\n./tutorial/quickstart/apps.py\n./tutorial/quickstart/migrations\n./tutorial/quickstart/migrations/__init__.py\n./tutorial/quickstart/models.py\n./tutorial/quickstart/tests.py\n./tutorial/quickstart/views.py\n./tutorial/settings.py\n./tutorial/urls.py\n./tutorial/wsgi.py\n\n\n\nIt may look unusual that the application has been created within the project directory. Using the project's namespace avoids name clashes with external modules (a topic that goes outside the scope of the quickstart).\n\n\nNow sync your database for the first time:\n\n\npython manage.py migrate\n\n\n\nWe'll also create an initial user named \nadmin\n with a password of \npassword123\n. We'll authenticate as that user later in our example.\n\n\npython manage.py createsuperuser --email admin@example.com --username admin\n\n\n\nOnce you've set up a database and the initial user is created and ready to go, open up the app's directory and we'll get coding...\n\n\nSerializers\n\n\nFirst up we're going to define some serializers. Let's create a new module named \ntutorial/quickstart/serializers.py\n that we'll use for our data representations.\n\n\nfrom django.contrib.auth.models import User, Group\nfrom rest_framework import serializers\n\n\nclass UserSerializer(serializers.HyperlinkedModelSerializer):\n class Meta:\n model = User\n fields = ('url', 'username', 'email', 'groups')\n\n\nclass GroupSerializer(serializers.HyperlinkedModelSerializer):\n class Meta:\n model = Group\n fields = ('url', 'name')\n\n\n\nNotice that we're using hyperlinked relations in this case with \nHyperlinkedModelSerializer\n. You can also use primary key and various other relationships, but hyperlinking is good RESTful design.\n\n\nViews\n\n\nRight, we'd better write some views then. Open \ntutorial/quickstart/views.py\n and get typing.\n\n\nfrom django.contrib.auth.models import User, Group\nfrom rest_framework import viewsets\nfrom tutorial.quickstart.serializers import UserSerializer, GroupSerializer\n\n\nclass UserViewSet(viewsets.ModelViewSet):\n \"\"\"\n API endpoint that allows users to be viewed or edited.\n \"\"\"\n queryset = User.objects.all().order_by('-date_joined')\n serializer_class = UserSerializer\n\n\nclass GroupViewSet(viewsets.ModelViewSet):\n \"\"\"\n API endpoint that allows groups to be viewed or edited.\n \"\"\"\n queryset = Group.objects.all()\n serializer_class = GroupSerializer\n\n\n\nRather than write multiple views we're grouping together all the common behavior into classes called \nViewSets\n.\n\n\nWe can easily break these down into individual views if we need to, but using viewsets keeps the view logic nicely organized as well as being very concise.\n\n\nURLs\n\n\nOkay, now let's wire up the API URLs. On to \ntutorial/urls.py\n...\n\n\nfrom django.urls import include, path\nfrom rest_framework import routers\nfrom tutorial.quickstart import views\n\nrouter = routers.DefaultRouter()\nrouter.register(r'users', views.UserViewSet)\nrouter.register(r'groups', views.GroupViewSet)\n\n# Wire up our API using automatic URL routing.\n# Additionally, we include login URLs for the browsable API.\nurlpatterns = [\n path('', include(router.urls)),\n path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))\n]\n\n\n\nBecause we're using viewsets instead of views, we can automatically generate the URL conf for our API, by simply registering the viewsets with a router class.\n\n\nAgain, if we need more control over the API URLs we can simply drop down to using regular class-based views, and writing the URL conf explicitly.\n\n\nFinally, we're including default login and logout views for use with the browsable API. That's optional, but useful if your API requires authentication and you want to use the browsable API.\n\n\nPagination\n\n\nPagination allows you to control how many objects per page are returned. To enable it add the following lines to \ntutorial/settings.py\n\n\nREST_FRAMEWORK = {\n 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',\n 'PAGE_SIZE': 10\n}\n\n\n\nSettings\n\n\nAdd \n'rest_framework'\n to \nINSTALLED_APPS\n. The settings module will be in \ntutorial/settings.py\n\n\nINSTALLED_APPS = (\n ...\n 'rest_framework',\n)\n\n\n\nOkay, we're done.\n\n\n\n\nTesting our API\n\n\nWe're now ready to test the API we've built. Let's fire up the server from the command line.\n\n\npython manage.py runserver\n\n\n\nWe can now access our API, both from the command-line, using tools like \ncurl\n...\n\n\nbash: curl -H 'Accept: application/json; indent=4' -u admin:password123 http://127.0.0.1:8000/users/\n{\n \"count\": 2,\n \"next\": null,\n \"previous\": null,\n \"results\": [\n {\n \"email\": \"admin@example.com\",\n \"groups\": [],\n \"url\": \"http://127.0.0.1:8000/users/1/\",\n \"username\": \"admin\"\n },\n {\n \"email\": \"tom@example.com\",\n \"groups\": [ ],\n \"url\": \"http://127.0.0.1:8000/users/2/\",\n \"username\": \"tom\"\n }\n ]\n}\n\n\n\nOr using the \nhttpie\n, command line tool...\n\n\nbash: http -a admin:password123 http://127.0.0.1:8000/users/\n\nHTTP/1.1 200 OK\n...\n{\n \"count\": 2,\n \"next\": null,\n \"previous\": null,\n \"results\": [\n {\n \"email\": \"admin@example.com\",\n \"groups\": [],\n \"url\": \"http://localhost:8000/users/1/\",\n \"username\": \"paul\"\n },\n {\n \"email\": \"tom@example.com\",\n \"groups\": [ ],\n \"url\": \"http://127.0.0.1:8000/users/2/\",\n \"username\": \"tom\"\n }\n ]\n}\n\n\n\nOr directly through the browser, by going to the URL \nhttp://127.0.0.1:8000/users/\n...\n\n\n\n\nIf you're working through the browser, make sure to login using the control in the top right corner.\n\n\nGreat, that was easy!\n\n\nIf you want to get a more in depth understanding of how REST framework fits together head on over to \nthe tutorial\n, or start browsing the \nAPI guide\n.",
"title": "Quickstart"
- },
+ },
{
- "location": "/tutorial/quickstart/#quickstart",
- "text": "We're going to create a simple API to allow admin users to view and edit the users and groups in the system.",
+ "location": "/tutorial/quickstart/#quickstart",
+ "text": "We're going to create a simple API to allow admin users to view and edit the users and groups in the system.",
"title": "Quickstart"
- },
+ },
{
- "location": "/tutorial/quickstart/#project-setup",
- "text": "Create a new Django project named tutorial , then start a new app called quickstart . # Create the project directory\nmkdir tutorial\ncd tutorial\n\n# Create a virtualenv to isolate our package dependencies locally\nvirtualenv env\nsource env/bin/activate # On Windows use `env\\Scripts\\activate`\n\n# Install Django and Django REST framework into the virtualenv\npip install django\npip install djangorestframework\n\n# Set up a new project with a single application\ndjango-admin startproject tutorial . # Note the trailing '.' character\ncd tutorial\ndjango-admin startapp quickstart\ncd .. The project layout should look like: $ pwd\n/tutorial\n$ find .\n.\n./manage.py\n./tutorial\n./tutorial/__init__.py\n./tutorial/quickstart\n./tutorial/quickstart/__init__.py\n./tutorial/quickstart/admin.py\n./tutorial/quickstart/apps.py\n./tutorial/quickstart/migrations\n./tutorial/quickstart/migrations/__init__.py\n./tutorial/quickstart/models.py\n./tutorial/quickstart/tests.py\n./tutorial/quickstart/views.py\n./tutorial/settings.py\n./tutorial/urls.py\n./tutorial/wsgi.py It may look unusual that the application has been created within the project directory. Using the project's namespace avoids name clashes with external modules (a topic that goes outside the scope of the quickstart). Now sync your database for the first time: python manage.py migrate We'll also create an initial user named admin with a password of password123 . We'll authenticate as that user later in our example. python manage.py createsuperuser --email admin@example.com --username admin Once you've set up a database and the initial user is created and ready to go, open up the app's directory and we'll get coding...",
+ "location": "/tutorial/quickstart/#project-setup",
+ "text": "Create a new Django project named tutorial , then start a new app called quickstart . # Create the project directory\nmkdir tutorial\ncd tutorial\n\n# Create a virtualenv to isolate our package dependencies locally\nvirtualenv env\nsource env/bin/activate # On Windows use `env\\Scripts\\activate`\n\n# Install Django and Django REST framework into the virtualenv\npip install django\npip install djangorestframework\n\n# Set up a new project with a single application\ndjango-admin startproject tutorial . # Note the trailing '.' character\ncd tutorial\ndjango-admin startapp quickstart\ncd .. The project layout should look like: $ pwd some path /tutorial\n$ find .\n.\n./manage.py\n./tutorial\n./tutorial/__init__.py\n./tutorial/quickstart\n./tutorial/quickstart/__init__.py\n./tutorial/quickstart/admin.py\n./tutorial/quickstart/apps.py\n./tutorial/quickstart/migrations\n./tutorial/quickstart/migrations/__init__.py\n./tutorial/quickstart/models.py\n./tutorial/quickstart/tests.py\n./tutorial/quickstart/views.py\n./tutorial/settings.py\n./tutorial/urls.py\n./tutorial/wsgi.py It may look unusual that the application has been created within the project directory. Using the project's namespace avoids name clashes with external modules (a topic that goes outside the scope of the quickstart). Now sync your database for the first time: python manage.py migrate We'll also create an initial user named admin with a password of password123 . We'll authenticate as that user later in our example. python manage.py createsuperuser --email admin@example.com --username admin Once you've set up a database and the initial user is created and ready to go, open up the app's directory and we'll get coding...",
"title": "Project setup"
- },
+ },
{
- "location": "/tutorial/quickstart/#serializers",
- "text": "First up we're going to define some serializers. Let's create a new module named tutorial/quickstart/serializers.py that we'll use for our data representations. from django.contrib.auth.models import User, Group\nfrom rest_framework import serializers\n\n\nclass UserSerializer(serializers.HyperlinkedModelSerializer):\n class Meta:\n model = User\n fields = ('url', 'username', 'email', 'groups')\n\n\nclass GroupSerializer(serializers.HyperlinkedModelSerializer):\n class Meta:\n model = Group\n fields = ('url', 'name') Notice that we're using hyperlinked relations in this case with HyperlinkedModelSerializer . You can also use primary key and various other relationships, but hyperlinking is good RESTful design.",
+ "location": "/tutorial/quickstart/#serializers",
+ "text": "First up we're going to define some serializers. Let's create a new module named tutorial/quickstart/serializers.py that we'll use for our data representations. from django.contrib.auth.models import User, Group\nfrom rest_framework import serializers\n\n\nclass UserSerializer(serializers.HyperlinkedModelSerializer):\n class Meta:\n model = User\n fields = ('url', 'username', 'email', 'groups')\n\n\nclass GroupSerializer(serializers.HyperlinkedModelSerializer):\n class Meta:\n model = Group\n fields = ('url', 'name') Notice that we're using hyperlinked relations in this case with HyperlinkedModelSerializer . You can also use primary key and various other relationships, but hyperlinking is good RESTful design.",
"title": "Serializers"
- },
+ },
{
- "location": "/tutorial/quickstart/#views",
- "text": "Right, we'd better write some views then. Open tutorial/quickstart/views.py and get typing. from django.contrib.auth.models import User, Group\nfrom rest_framework import viewsets\nfrom tutorial.quickstart.serializers import UserSerializer, GroupSerializer\n\n\nclass UserViewSet(viewsets.ModelViewSet):\n \"\"\"\n API endpoint that allows users to be viewed or edited.\n \"\"\"\n queryset = User.objects.all().order_by('-date_joined')\n serializer_class = UserSerializer\n\n\nclass GroupViewSet(viewsets.ModelViewSet):\n \"\"\"\n API endpoint that allows groups to be viewed or edited.\n \"\"\"\n queryset = Group.objects.all()\n serializer_class = GroupSerializer Rather than write multiple views we're grouping together all the common behavior into classes called ViewSets . We can easily break these down into individual views if we need to, but using viewsets keeps the view logic nicely organized as well as being very concise.",
+ "location": "/tutorial/quickstart/#views",
+ "text": "Right, we'd better write some views then. Open tutorial/quickstart/views.py and get typing. from django.contrib.auth.models import User, Group\nfrom rest_framework import viewsets\nfrom tutorial.quickstart.serializers import UserSerializer, GroupSerializer\n\n\nclass UserViewSet(viewsets.ModelViewSet):\n \"\"\"\n API endpoint that allows users to be viewed or edited.\n \"\"\"\n queryset = User.objects.all().order_by('-date_joined')\n serializer_class = UserSerializer\n\n\nclass GroupViewSet(viewsets.ModelViewSet):\n \"\"\"\n API endpoint that allows groups to be viewed or edited.\n \"\"\"\n queryset = Group.objects.all()\n serializer_class = GroupSerializer Rather than write multiple views we're grouping together all the common behavior into classes called ViewSets . We can easily break these down into individual views if we need to, but using viewsets keeps the view logic nicely organized as well as being very concise.",
"title": "Views"
- },
+ },
{
- "location": "/tutorial/quickstart/#urls",
- "text": "Okay, now let's wire up the API URLs. On to tutorial/urls.py ... from django.urls import include, path\nfrom rest_framework import routers\nfrom tutorial.quickstart import views\n\nrouter = routers.DefaultRouter()\nrouter.register(r'users', views.UserViewSet)\nrouter.register(r'groups', views.GroupViewSet)\n\n# Wire up our API using automatic URL routing.\n# Additionally, we include login URLs for the browsable API.\nurlpatterns = [\n path('', include(router.urls)),\n path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))\n] Because we're using viewsets instead of views, we can automatically generate the URL conf for our API, by simply registering the viewsets with a router class. Again, if we need more control over the API URLs we can simply drop down to using regular class-based views, and writing the URL conf explicitly. Finally, we're including default login and logout views for use with the browsable API. That's optional, but useful if your API requires authentication and you want to use the browsable API.",
+ "location": "/tutorial/quickstart/#urls",
+ "text": "Okay, now let's wire up the API URLs. On to tutorial/urls.py ... from django.urls import include, path\nfrom rest_framework import routers\nfrom tutorial.quickstart import views\n\nrouter = routers.DefaultRouter()\nrouter.register(r'users', views.UserViewSet)\nrouter.register(r'groups', views.GroupViewSet)\n\n# Wire up our API using automatic URL routing.\n# Additionally, we include login URLs for the browsable API.\nurlpatterns = [\n path('', include(router.urls)),\n path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))\n] Because we're using viewsets instead of views, we can automatically generate the URL conf for our API, by simply registering the viewsets with a router class. Again, if we need more control over the API URLs we can simply drop down to using regular class-based views, and writing the URL conf explicitly. Finally, we're including default login and logout views for use with the browsable API. That's optional, but useful if your API requires authentication and you want to use the browsable API.",
"title": "URLs"
- },
+ },
{
- "location": "/tutorial/quickstart/#pagination",
- "text": "Pagination allows you to control how many objects per page are returned. To enable it add the following lines to tutorial/settings.py REST_FRAMEWORK = {\n 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',\n 'PAGE_SIZE': 10\n}",
+ "location": "/tutorial/quickstart/#pagination",
+ "text": "Pagination allows you to control how many objects per page are returned. To enable it add the following lines to tutorial/settings.py REST_FRAMEWORK = {\n 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',\n 'PAGE_SIZE': 10\n}",
"title": "Pagination"
- },
+ },
{
- "location": "/tutorial/quickstart/#settings",
- "text": "Add 'rest_framework' to INSTALLED_APPS . The settings module will be in tutorial/settings.py INSTALLED_APPS = (\n ...\n 'rest_framework',\n) Okay, we're done.",
+ "location": "/tutorial/quickstart/#settings",
+ "text": "Add 'rest_framework' to INSTALLED_APPS . The settings module will be in tutorial/settings.py INSTALLED_APPS = (\n ...\n 'rest_framework',\n) Okay, we're done.",
"title": "Settings"
- },
+ },
{
- "location": "/tutorial/quickstart/#testing-our-api",
- "text": "We're now ready to test the API we've built. Let's fire up the server from the command line. python manage.py runserver We can now access our API, both from the command-line, using tools like curl ... bash: curl -H 'Accept: application/json; indent=4' -u admin:password123 http://127.0.0.1:8000/users/\n{\n \"count\": 2,\n \"next\": null,\n \"previous\": null,\n \"results\": [\n {\n \"email\": \"admin@example.com\",\n \"groups\": [],\n \"url\": \"http://127.0.0.1:8000/users/1/\",\n \"username\": \"admin\"\n },\n {\n \"email\": \"tom@example.com\",\n \"groups\": [ ],\n \"url\": \"http://127.0.0.1:8000/users/2/\",\n \"username\": \"tom\"\n }\n ]\n} Or using the httpie , command line tool... bash: http -a admin:password123 http://127.0.0.1:8000/users/\n\nHTTP/1.1 200 OK\n...\n{\n \"count\": 2,\n \"next\": null,\n \"previous\": null,\n \"results\": [\n {\n \"email\": \"admin@example.com\",\n \"groups\": [],\n \"url\": \"http://localhost:8000/users/1/\",\n \"username\": \"paul\"\n },\n {\n \"email\": \"tom@example.com\",\n \"groups\": [ ],\n \"url\": \"http://127.0.0.1:8000/users/2/\",\n \"username\": \"tom\"\n }\n ]\n} Or directly through the browser, by going to the URL http://127.0.0.1:8000/users/ ... If you're working through the browser, make sure to login using the control in the top right corner. Great, that was easy! If you want to get a more in depth understanding of how REST framework fits together head on over to the tutorial , or start browsing the API guide .",
+ "location": "/tutorial/quickstart/#testing-our-api",
+ "text": "We're now ready to test the API we've built. Let's fire up the server from the command line. python manage.py runserver We can now access our API, both from the command-line, using tools like curl ... bash: curl -H 'Accept: application/json; indent=4' -u admin:password123 http://127.0.0.1:8000/users/\n{\n \"count\": 2,\n \"next\": null,\n \"previous\": null,\n \"results\": [\n {\n \"email\": \"admin@example.com\",\n \"groups\": [],\n \"url\": \"http://127.0.0.1:8000/users/1/\",\n \"username\": \"admin\"\n },\n {\n \"email\": \"tom@example.com\",\n \"groups\": [ ],\n \"url\": \"http://127.0.0.1:8000/users/2/\",\n \"username\": \"tom\"\n }\n ]\n} Or using the httpie , command line tool... bash: http -a admin:password123 http://127.0.0.1:8000/users/\n\nHTTP/1.1 200 OK\n...\n{\n \"count\": 2,\n \"next\": null,\n \"previous\": null,\n \"results\": [\n {\n \"email\": \"admin@example.com\",\n \"groups\": [],\n \"url\": \"http://localhost:8000/users/1/\",\n \"username\": \"paul\"\n },\n {\n \"email\": \"tom@example.com\",\n \"groups\": [ ],\n \"url\": \"http://127.0.0.1:8000/users/2/\",\n \"username\": \"tom\"\n }\n ]\n} Or directly through the browser, by going to the URL http://127.0.0.1:8000/users/ ... If you're working through the browser, make sure to login using the control in the top right corner. Great, that was easy! If you want to get a more in depth understanding of how REST framework fits together head on over to the tutorial , or start browsing the API guide .",
"title": "Testing our API"
- },
+ },
{
- "location": "/tutorial/1-serialization/",
- "text": "Tutorial 1: Serialization\n\n\nIntroduction\n\n\nThis tutorial will cover creating a simple pastebin code highlighting Web API. Along the way it will introduce the various components that make up REST framework, and give you a comprehensive understanding of how everything fits together.\n\n\nThe tutorial is fairly in-depth, so you should probably get a cookie and a cup of your favorite brew before getting started. If you just want a quick overview, you should head over to the \nquickstart\n documentation instead.\n\n\n\n\nNote\n: The code for this tutorial is available in the \ntomchristie/rest-framework-tutorial\n repository on GitHub. The completed implementation is also online as a sandbox version for testing, \navailable here\n.\n\n\n\n\nSetting up a new environment\n\n\nBefore we do anything else we'll create a new virtual environment, using \nvirtualenv\n. This will make sure our package configuration is kept nicely isolated from any other projects we're working on.\n\n\nvirtualenv env\nsource env/bin/activate\n\n\n\nNow that we're inside a virtualenv environment, we can install our package requirements.\n\n\npip install django\npip install djangorestframework\npip install pygments # We'll be using this for the code highlighting\n\n\n\nNote:\n To exit the virtualenv environment at any time, just type \ndeactivate\n. For more information see the \nvirtualenv documentation\n.\n\n\nGetting started\n\n\nOkay, we're ready to get coding.\nTo get started, let's create a new project to work with.\n\n\ncd ~\ndjango-admin startproject tutorial\ncd tutorial\n\n\n\nOnce that's done we can create an app that we'll use to create a simple Web API.\n\n\npython manage.py startapp snippets\n\n\n\nWe'll need to add our new \nsnippets\n app and the \nrest_framework\n app to \nINSTALLED_APPS\n. Let's edit the \ntutorial/settings.py\n file:\n\n\nINSTALLED_APPS = (\n ...\n 'rest_framework',\n 'snippets.apps.SnippetsConfig',\n)\n\n\n\nOkay, we're ready to roll.\n\n\nCreating a model to work with\n\n\nFor the purposes of this tutorial we're going to start by creating a simple \nSnippet\n model that is used to store code snippets. Go ahead and edit the \nsnippets/models.py\n file. Note: Good programming practices include comments. Although you will find them in our repository version of this tutorial code, we have omitted them here to focus on the code itself.\n\n\nfrom django.db import models\nfrom pygments.lexers import get_all_lexers\nfrom pygments.styles import get_all_styles\n\nLEXERS = [item for item in get_all_lexers() if item[1]]\nLANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])\nSTYLE_CHOICES = sorted((item, item) for item in get_all_styles())\n\n\nclass Snippet(models.Model):\n created = models.DateTimeField(auto_now_add=True)\n title = models.CharField(max_length=100, blank=True, default='')\n code = models.TextField()\n linenos = models.BooleanField(default=False)\n language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)\n style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)\n\n class Meta:\n ordering = ('created',)\n\n\n\nWe'll also need to create an initial migration for our snippet model, and sync the database for the first time.\n\n\npython manage.py makemigrations snippets\npython manage.py migrate\n\n\n\nCreating a Serializer class\n\n\nThe first thing we need to get started on our Web API is to provide a way of serializing and deserializing the snippet instances into representations such as \njson\n. We can do this by declaring serializers that work very similar to Django's forms. Create a file in the \nsnippets\n directory named \nserializers.py\n and add the following.\n\n\nfrom rest_framework import serializers\nfrom snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES\n\n\nclass SnippetSerializer(serializers.Serializer):\n id = serializers.IntegerField(read_only=True)\n title = serializers.CharField(required=False, allow_blank=True, max_length=100)\n code = serializers.CharField(style={'base_template': 'textarea.html'})\n linenos = serializers.BooleanField(required=False)\n language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')\n style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')\n\n def create(self, validated_data):\n \"\"\"\n Create and return a new `Snippet` instance, given the validated data.\n \"\"\"\n return Snippet.objects.create(**validated_data)\n\n def update(self, instance, validated_data):\n \"\"\"\n Update and return an existing `Snippet` instance, given the validated data.\n \"\"\"\n instance.title = validated_data.get('title', instance.title)\n instance.code = validated_data.get('code', instance.code)\n instance.linenos = validated_data.get('linenos', instance.linenos)\n instance.language = validated_data.get('language', instance.language)\n instance.style = validated_data.get('style', instance.style)\n instance.save()\n return instance\n\n\n\nThe first part of the serializer class defines the fields that get serialized/deserialized. The \ncreate()\n and \nupdate()\n methods define how fully fledged instances are created or modified when calling \nserializer.save()\n\n\nA serializer class is very similar to a Django \nForm\n class, and includes similar validation flags on the various fields, such as \nrequired\n, \nmax_length\n and \ndefault\n.\n\n\nThe field flags can also control how the serializer should be displayed in certain circumstances, such as when rendering to HTML. The \n{'base_template': 'textarea.html'}\n flag above is equivalent to using \nwidget=widgets.Textarea\n on a Django \nForm\n class. This is particularly useful for controlling how the browsable API should be displayed, as we'll see later in the tutorial.\n\n\nWe can actually also save ourselves some time by using the \nModelSerializer\n class, as we'll see later, but for now we'll keep our serializer definition explicit.\n\n\nWorking with Serializers\n\n\nBefore we go any further we'll familiarize ourselves with using our new Serializer class. Let's drop into the Django shell.\n\n\npython manage.py shell\n\n\n\nOkay, once we've got a few imports out of the way, let's create a couple of code snippets to work with.\n\n\nfrom snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\nfrom rest_framework.renderers import JSONRenderer\nfrom rest_framework.parsers import JSONParser\n\nsnippet = Snippet(code='foo = \"bar\"\\n')\nsnippet.save()\n\nsnippet = Snippet(code='print(\"hello, world\")\\n')\nsnippet.save()\n\n\n\nWe've now got a few snippet instances to play with. Let's take a look at serializing one of those instances.\n\n\nserializer = SnippetSerializer(snippet)\nserializer.data\n# {'id': 2, 'title': '', 'code': 'print(\"hello, world\")\\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}\n\n\n\nAt this point we've translated the model instance into Python native datatypes. To finalize the serialization process we render the data into \njson\n.\n\n\ncontent = JSONRenderer().render(serializer.data)\ncontent\n# b'{\"id\": 2, \"title\": \"\", \"code\": \"print(\\\\\"hello, world\\\\\")\\\\n\", \"linenos\": false, \"language\": \"python\", \"style\": \"friendly\"}'\n\n\n\nDeserialization is similar. First we parse a stream into Python native datatypes...\n\n\nimport io\n\nstream = io.BytesIO(content)\ndata = JSONParser().parse(stream)\n\n\n\n...then we restore those native datatypes into a fully populated object instance.\n\n\nserializer = SnippetSerializer(data=data)\nserializer.is_valid()\n# True\nserializer.validated_data\n# OrderedDict([('title', ''), ('code', 'print(\"hello, world\")\\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])\nserializer.save()\n# \n\n\n\nNotice how similar the API is to working with forms. The similarity should become even more apparent when we start writing views that use our serializer.\n\n\nWe can also serialize querysets instead of model instances. To do so we simply add a \nmany=True\n flag to the serializer arguments.\n\n\nserializer = SnippetSerializer(Snippet.objects.all(), many=True)\nserializer.data\n# [OrderedDict([('id', 1), ('title', ''), ('code', 'foo = \"bar\"\\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', ''), ('code', 'print(\"hello, world\")\\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 3), ('title', ''), ('code', 'print(\"hello, world\")'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])]\n\n\n\nUsing ModelSerializers\n\n\nOur \nSnippetSerializer\n class is replicating a lot of information that's also contained in the \nSnippet\n model. It would be nice if we could keep our code a bit more concise.\n\n\nIn the same way that Django provides both \nForm\n classes and \nModelForm\n classes, REST framework includes both \nSerializer\n classes, and \nModelSerializer\n classes.\n\n\nLet's look at refactoring our serializer using the \nModelSerializer\n class.\nOpen the file \nsnippets/serializers.py\n again, and replace the \nSnippetSerializer\n class with the following.\n\n\nclass SnippetSerializer(serializers.ModelSerializer):\n class Meta:\n model = Snippet\n fields = ('id', 'title', 'code', 'linenos', 'language', 'style')\n\n\n\nOne nice property that serializers have is that you can inspect all the fields in a serializer instance, by printing its representation. Open the Django shell with \npython manage.py shell\n, then try the following:\n\n\nfrom snippets.serializers import SnippetSerializer\nserializer = SnippetSerializer()\nprint(repr(serializer))\n# SnippetSerializer():\n# id = IntegerField(label='ID', read_only=True)\n# title = CharField(allow_blank=True, max_length=100, required=False)\n# code = CharField(style={'base_template': 'textarea.html'})\n# linenos = BooleanField(required=False)\n# language = ChoiceField(choices=[('Clipper', 'FoxPro'), ('Cucumber', 'Gherkin'), ('RobotFramework', 'RobotFramework'), ('abap', 'ABAP'), ('ada', 'Ada')...\n# style = ChoiceField(choices=[('autumn', 'autumn'), ('borland', 'borland'), ('bw', 'bw'), ('colorful', 'colorful')...\n\n\n\nIt's important to remember that \nModelSerializer\n classes don't do anything particularly magical, they are simply a shortcut for creating serializer classes:\n\n\n\n\nAn automatically determined set of fields.\n\n\nSimple default implementations for the \ncreate()\n and \nupdate()\n methods.\n\n\n\n\nWriting regular Django views using our Serializer\n\n\nLet's see how we can write some API views using our new Serializer class.\nFor the moment we won't use any of REST framework's other features, we'll just write the views as regular Django views.\n\n\nEdit the \nsnippets/views.py\n file, and add the following.\n\n\nfrom django.http import HttpResponse, JsonResponse\nfrom django.views.decorators.csrf import csrf_exempt\nfrom rest_framework.renderers import JSONRenderer\nfrom rest_framework.parsers import JSONParser\nfrom snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\n\n\n\nThe root of our API is going to be a view that supports listing all the existing snippets, or creating a new snippet.\n\n\n@csrf_exempt\ndef snippet_list(request):\n \"\"\"\n List all code snippets, or create a new snippet.\n \"\"\"\n if request.method == 'GET':\n snippets = Snippet.objects.all()\n serializer = SnippetSerializer(snippets, many=True)\n return JsonResponse(serializer.data, safe=False)\n\n elif request.method == 'POST':\n data = JSONParser().parse(request)\n serializer = SnippetSerializer(data=data)\n if serializer.is_valid():\n serializer.save()\n return JsonResponse(serializer.data, status=201)\n return JsonResponse(serializer.errors, status=400)\n\n\n\nNote that because we want to be able to POST to this view from clients that won't have a CSRF token we need to mark the view as \ncsrf_exempt\n. This isn't something that you'd normally want to do, and REST framework views actually use more sensible behavior than this, but it'll do for our purposes right now.\n\n\nWe'll also need a view which corresponds to an individual snippet, and can be used to retrieve, update or delete the snippet.\n\n\n@csrf_exempt\ndef snippet_detail(request, pk):\n \"\"\"\n Retrieve, update or delete a code snippet.\n \"\"\"\n try:\n snippet = Snippet.objects.get(pk=pk)\n except Snippet.DoesNotExist:\n return HttpResponse(status=404)\n\n if request.method == 'GET':\n serializer = SnippetSerializer(snippet)\n return JsonResponse(serializer.data)\n\n elif request.method == 'PUT':\n data = JSONParser().parse(request)\n serializer = SnippetSerializer(snippet, data=data)\n if serializer.is_valid():\n serializer.save()\n return JsonResponse(serializer.data)\n return JsonResponse(serializer.errors, status=400)\n\n elif request.method == 'DELETE':\n snippet.delete()\n return HttpResponse(status=204)\n\n\n\nFinally we need to wire these views up. Create the \nsnippets/urls.py\n file:\n\n\nfrom django.urls import path\nfrom snippets import views\n\nurlpatterns = [\n path('snippets/', views.snippet_list),\n path('snippets//', views.snippet_detail),\n]\n\n\n\nWe also need to wire up the root urlconf, in the \ntutorial/urls.py\n file, to include our snippet app's URLs.\n\n\nfrom django.urls import path, include\n\nurlpatterns = [\n path('', include('snippets.urls')),\n]\n\n\n\nIt's worth noting that there are a couple of edge cases we're not dealing with properly at the moment. If we send malformed \njson\n, or if a request is made with a method that the view doesn't handle, then we'll end up with a 500 \"server error\" response. Still, this'll do for now.\n\n\nTesting our first attempt at a Web API\n\n\nNow we can start up a sample server that serves our snippets.\n\n\nQuit out of the shell...\n\n\nquit()\n\n\n\n...and start up Django's development server.\n\n\npython manage.py runserver\n\nValidating models...\n\n0 errors found\nDjango version 1.11, using settings 'tutorial.settings'\nDevelopment server is running at http://127.0.0.1:8000/\nQuit the server with CONTROL-C.\n\n\n\nIn another terminal window, we can test the server.\n\n\nWe can test our API using \ncurl\n or \nhttpie\n. Httpie is a user friendly http client that's written in Python. Let's install that.\n\n\nYou can install httpie using pip:\n\n\npip install httpie\n\n\n\nFinally, we can get a list of all of the snippets:\n\n\nhttp http://127.0.0.1:8000/snippets/\n\nHTTP/1.1 200 OK\n...\n[\n {\n \"id\": 1,\n \"title\": \"\",\n \"code\": \"foo = \\\"bar\\\"\\n\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n },\n {\n \"id\": 2,\n \"title\": \"\",\n \"code\": \"print(\\\"hello, world\\\")\\n\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n }\n]\n\n\n\nOr we can get a particular snippet by referencing its id:\n\n\nhttp http://127.0.0.1:8000/snippets/2/\n\nHTTP/1.1 200 OK\n...\n{\n \"id\": 2,\n \"title\": \"\",\n \"code\": \"print(\\\"hello, world\\\")\\n\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n}\n\n\n\nSimilarly, you can have the same json displayed by visiting these URLs in a web browser.\n\n\nWhere are we now\n\n\nWe're doing okay so far, we've got a serialization API that feels pretty similar to Django's Forms API, and some regular Django views.\n\n\nOur API views don't do anything particularly special at the moment, beyond serving \njson\n responses, and there are some error handling edge cases we'd still like to clean up, but it's a functioning Web API.\n\n\nWe'll see how we can start to improve things in \npart 2 of the tutorial\n.",
+ "location": "/tutorial/1-serialization/",
+ "text": "Tutorial 1: Serialization\n\n\nIntroduction\n\n\nThis tutorial will cover creating a simple pastebin code highlighting Web API. Along the way it will introduce the various components that make up REST framework, and give you a comprehensive understanding of how everything fits together.\n\n\nThe tutorial is fairly in-depth, so you should probably get a cookie and a cup of your favorite brew before getting started. If you just want a quick overview, you should head over to the \nquickstart\n documentation instead.\n\n\n\n\nNote\n: The code for this tutorial is available in the \nencode/rest-framework-tutorial\n repository on GitHub. The completed implementation is also online as a sandbox version for testing, \navailable here\n.\n\n\n\n\nSetting up a new environment\n\n\nBefore we do anything else we'll create a new virtual environment, using \nvirtualenv\n. This will make sure our package configuration is kept nicely isolated from any other projects we're working on.\n\n\nvirtualenv env\nsource env/bin/activate\n\n\n\nNow that we're inside a virtualenv environment, we can install our package requirements.\n\n\npip install django\npip install djangorestframework\npip install pygments # We'll be using this for the code highlighting\n\n\n\nNote:\n To exit the virtualenv environment at any time, just type \ndeactivate\n. For more information see the \nvirtualenv documentation\n.\n\n\nGetting started\n\n\nOkay, we're ready to get coding.\nTo get started, let's create a new project to work with.\n\n\ncd ~\ndjango-admin startproject tutorial\ncd tutorial\n\n\n\nOnce that's done we can create an app that we'll use to create a simple Web API.\n\n\npython manage.py startapp snippets\n\n\n\nWe'll need to add our new \nsnippets\n app and the \nrest_framework\n app to \nINSTALLED_APPS\n. Let's edit the \ntutorial/settings.py\n file:\n\n\nINSTALLED_APPS = (\n ...\n 'rest_framework',\n 'snippets.apps.SnippetsConfig',\n)\n\n\n\nOkay, we're ready to roll.\n\n\nCreating a model to work with\n\n\nFor the purposes of this tutorial we're going to start by creating a simple \nSnippet\n model that is used to store code snippets. Go ahead and edit the \nsnippets/models.py\n file. Note: Good programming practices include comments. Although you will find them in our repository version of this tutorial code, we have omitted them here to focus on the code itself.\n\n\nfrom django.db import models\nfrom pygments.lexers import get_all_lexers\nfrom pygments.styles import get_all_styles\n\nLEXERS = [item for item in get_all_lexers() if item[1]]\nLANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])\nSTYLE_CHOICES = sorted((item, item) for item in get_all_styles())\n\n\nclass Snippet(models.Model):\n created = models.DateTimeField(auto_now_add=True)\n title = models.CharField(max_length=100, blank=True, default='')\n code = models.TextField()\n linenos = models.BooleanField(default=False)\n language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)\n style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)\n\n class Meta:\n ordering = ('created',)\n\n\n\nWe'll also need to create an initial migration for our snippet model, and sync the database for the first time.\n\n\npython manage.py makemigrations snippets\npython manage.py migrate\n\n\n\nCreating a Serializer class\n\n\nThe first thing we need to get started on our Web API is to provide a way of serializing and deserializing the snippet instances into representations such as \njson\n. We can do this by declaring serializers that work very similar to Django's forms. Create a file in the \nsnippets\n directory named \nserializers.py\n and add the following.\n\n\nfrom rest_framework import serializers\nfrom snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES\n\n\nclass SnippetSerializer(serializers.Serializer):\n id = serializers.IntegerField(read_only=True)\n title = serializers.CharField(required=False, allow_blank=True, max_length=100)\n code = serializers.CharField(style={'base_template': 'textarea.html'})\n linenos = serializers.BooleanField(required=False)\n language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')\n style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')\n\n def create(self, validated_data):\n \"\"\"\n Create and return a new `Snippet` instance, given the validated data.\n \"\"\"\n return Snippet.objects.create(**validated_data)\n\n def update(self, instance, validated_data):\n \"\"\"\n Update and return an existing `Snippet` instance, given the validated data.\n \"\"\"\n instance.title = validated_data.get('title', instance.title)\n instance.code = validated_data.get('code', instance.code)\n instance.linenos = validated_data.get('linenos', instance.linenos)\n instance.language = validated_data.get('language', instance.language)\n instance.style = validated_data.get('style', instance.style)\n instance.save()\n return instance\n\n\n\nThe first part of the serializer class defines the fields that get serialized/deserialized. The \ncreate()\n and \nupdate()\n methods define how fully fledged instances are created or modified when calling \nserializer.save()\n\n\nA serializer class is very similar to a Django \nForm\n class, and includes similar validation flags on the various fields, such as \nrequired\n, \nmax_length\n and \ndefault\n.\n\n\nThe field flags can also control how the serializer should be displayed in certain circumstances, such as when rendering to HTML. The \n{'base_template': 'textarea.html'}\n flag above is equivalent to using \nwidget=widgets.Textarea\n on a Django \nForm\n class. This is particularly useful for controlling how the browsable API should be displayed, as we'll see later in the tutorial.\n\n\nWe can actually also save ourselves some time by using the \nModelSerializer\n class, as we'll see later, but for now we'll keep our serializer definition explicit.\n\n\nWorking with Serializers\n\n\nBefore we go any further we'll familiarize ourselves with using our new Serializer class. Let's drop into the Django shell.\n\n\npython manage.py shell\n\n\n\nOkay, once we've got a few imports out of the way, let's create a couple of code snippets to work with.\n\n\nfrom snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\nfrom rest_framework.renderers import JSONRenderer\nfrom rest_framework.parsers import JSONParser\n\nsnippet = Snippet(code='foo = \"bar\"\\n')\nsnippet.save()\n\nsnippet = Snippet(code='print(\"hello, world\")\\n')\nsnippet.save()\n\n\n\nWe've now got a few snippet instances to play with. Let's take a look at serializing one of those instances.\n\n\nserializer = SnippetSerializer(snippet)\nserializer.data\n# {'id': 2, 'title': '', 'code': 'print(\"hello, world\")\\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}\n\n\n\nAt this point we've translated the model instance into Python native datatypes. To finalize the serialization process we render the data into \njson\n.\n\n\ncontent = JSONRenderer().render(serializer.data)\ncontent\n# b'{\"id\": 2, \"title\": \"\", \"code\": \"print(\\\\\"hello, world\\\\\")\\\\n\", \"linenos\": false, \"language\": \"python\", \"style\": \"friendly\"}'\n\n\n\nDeserialization is similar. First we parse a stream into Python native datatypes...\n\n\nimport io\n\nstream = io.BytesIO(content)\ndata = JSONParser().parse(stream)\n\n\n\n...then we restore those native datatypes into a fully populated object instance.\n\n\nserializer = SnippetSerializer(data=data)\nserializer.is_valid()\n# True\nserializer.validated_data\n# OrderedDict([('title', ''), ('code', 'print(\"hello, world\")\\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])\nserializer.save()\n# \nSnippet: Snippet object\n\n\n\n\nNotice how similar the API is to working with forms. The similarity should become even more apparent when we start writing views that use our serializer.\n\n\nWe can also serialize querysets instead of model instances. To do so we simply add a \nmany=True\n flag to the serializer arguments.\n\n\nserializer = SnippetSerializer(Snippet.objects.all(), many=True)\nserializer.data\n# [OrderedDict([('id', 1), ('title', ''), ('code', 'foo = \"bar\"\\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', ''), ('code', 'print(\"hello, world\")\\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 3), ('title', ''), ('code', 'print(\"hello, world\")'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])]\n\n\n\nUsing ModelSerializers\n\n\nOur \nSnippetSerializer\n class is replicating a lot of information that's also contained in the \nSnippet\n model. It would be nice if we could keep our code a bit more concise.\n\n\nIn the same way that Django provides both \nForm\n classes and \nModelForm\n classes, REST framework includes both \nSerializer\n classes, and \nModelSerializer\n classes.\n\n\nLet's look at refactoring our serializer using the \nModelSerializer\n class.\nOpen the file \nsnippets/serializers.py\n again, and replace the \nSnippetSerializer\n class with the following.\n\n\nclass SnippetSerializer(serializers.ModelSerializer):\n class Meta:\n model = Snippet\n fields = ('id', 'title', 'code', 'linenos', 'language', 'style')\n\n\n\nOne nice property that serializers have is that you can inspect all the fields in a serializer instance, by printing its representation. Open the Django shell with \npython manage.py shell\n, then try the following:\n\n\nfrom snippets.serializers import SnippetSerializer\nserializer = SnippetSerializer()\nprint(repr(serializer))\n# SnippetSerializer():\n# id = IntegerField(label='ID', read_only=True)\n# title = CharField(allow_blank=True, max_length=100, required=False)\n# code = CharField(style={'base_template': 'textarea.html'})\n# linenos = BooleanField(required=False)\n# language = ChoiceField(choices=[('Clipper', 'FoxPro'), ('Cucumber', 'Gherkin'), ('RobotFramework', 'RobotFramework'), ('abap', 'ABAP'), ('ada', 'Ada')...\n# style = ChoiceField(choices=[('autumn', 'autumn'), ('borland', 'borland'), ('bw', 'bw'), ('colorful', 'colorful')...\n\n\n\nIt's important to remember that \nModelSerializer\n classes don't do anything particularly magical, they are simply a shortcut for creating serializer classes:\n\n\n\n\nAn automatically determined set of fields.\n\n\nSimple default implementations for the \ncreate()\n and \nupdate()\n methods.\n\n\n\n\nWriting regular Django views using our Serializer\n\n\nLet's see how we can write some API views using our new Serializer class.\nFor the moment we won't use any of REST framework's other features, we'll just write the views as regular Django views.\n\n\nEdit the \nsnippets/views.py\n file, and add the following.\n\n\nfrom django.http import HttpResponse, JsonResponse\nfrom django.views.decorators.csrf import csrf_exempt\nfrom rest_framework.parsers import JSONParser\nfrom snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\n\n\n\nThe root of our API is going to be a view that supports listing all the existing snippets, or creating a new snippet.\n\n\n@csrf_exempt\ndef snippet_list(request):\n \"\"\"\n List all code snippets, or create a new snippet.\n \"\"\"\n if request.method == 'GET':\n snippets = Snippet.objects.all()\n serializer = SnippetSerializer(snippets, many=True)\n return JsonResponse(serializer.data, safe=False)\n\n elif request.method == 'POST':\n data = JSONParser().parse(request)\n serializer = SnippetSerializer(data=data)\n if serializer.is_valid():\n serializer.save()\n return JsonResponse(serializer.data, status=201)\n return JsonResponse(serializer.errors, status=400)\n\n\n\nNote that because we want to be able to POST to this view from clients that won't have a CSRF token we need to mark the view as \ncsrf_exempt\n. This isn't something that you'd normally want to do, and REST framework views actually use more sensible behavior than this, but it'll do for our purposes right now.\n\n\nWe'll also need a view which corresponds to an individual snippet, and can be used to retrieve, update or delete the snippet.\n\n\n@csrf_exempt\ndef snippet_detail(request, pk):\n \"\"\"\n Retrieve, update or delete a code snippet.\n \"\"\"\n try:\n snippet = Snippet.objects.get(pk=pk)\n except Snippet.DoesNotExist:\n return HttpResponse(status=404)\n\n if request.method == 'GET':\n serializer = SnippetSerializer(snippet)\n return JsonResponse(serializer.data)\n\n elif request.method == 'PUT':\n data = JSONParser().parse(request)\n serializer = SnippetSerializer(snippet, data=data)\n if serializer.is_valid():\n serializer.save()\n return JsonResponse(serializer.data)\n return JsonResponse(serializer.errors, status=400)\n\n elif request.method == 'DELETE':\n snippet.delete()\n return HttpResponse(status=204)\n\n\n\nFinally we need to wire these views up. Create the \nsnippets/urls.py\n file:\n\n\nfrom django.urls import path\nfrom snippets import views\n\nurlpatterns = [\n path('snippets/', views.snippet_list),\n path('snippets/\nint:pk\n/', views.snippet_detail),\n]\n\n\n\nWe also need to wire up the root urlconf, in the \ntutorial/urls.py\n file, to include our snippet app's URLs.\n\n\nfrom django.urls import path, include\n\nurlpatterns = [\n path('', include('snippets.urls')),\n]\n\n\n\nIt's worth noting that there are a couple of edge cases we're not dealing with properly at the moment. If we send malformed \njson\n, or if a request is made with a method that the view doesn't handle, then we'll end up with a 500 \"server error\" response. Still, this'll do for now.\n\n\nTesting our first attempt at a Web API\n\n\nNow we can start up a sample server that serves our snippets.\n\n\nQuit out of the shell...\n\n\nquit()\n\n\n\n...and start up Django's development server.\n\n\npython manage.py runserver\n\nValidating models...\n\n0 errors found\nDjango version 1.11, using settings 'tutorial.settings'\nDevelopment server is running at http://127.0.0.1:8000/\nQuit the server with CONTROL-C.\n\n\n\nIn another terminal window, we can test the server.\n\n\nWe can test our API using \ncurl\n or \nhttpie\n. Httpie is a user friendly http client that's written in Python. Let's install that.\n\n\nYou can install httpie using pip:\n\n\npip install httpie\n\n\n\nFinally, we can get a list of all of the snippets:\n\n\nhttp http://127.0.0.1:8000/snippets/\n\nHTTP/1.1 200 OK\n...\n[\n {\n \"id\": 1,\n \"title\": \"\",\n \"code\": \"foo = \\\"bar\\\"\\n\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n },\n {\n \"id\": 2,\n \"title\": \"\",\n \"code\": \"print(\\\"hello, world\\\")\\n\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n }\n]\n\n\n\nOr we can get a particular snippet by referencing its id:\n\n\nhttp http://127.0.0.1:8000/snippets/2/\n\nHTTP/1.1 200 OK\n...\n{\n \"id\": 2,\n \"title\": \"\",\n \"code\": \"print(\\\"hello, world\\\")\\n\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n}\n\n\n\nSimilarly, you can have the same json displayed by visiting these URLs in a web browser.\n\n\nWhere are we now\n\n\nWe're doing okay so far, we've got a serialization API that feels pretty similar to Django's Forms API, and some regular Django views.\n\n\nOur API views don't do anything particularly special at the moment, beyond serving \njson\n responses, and there are some error handling edge cases we'd still like to clean up, but it's a functioning Web API.\n\n\nWe'll see how we can start to improve things in \npart 2 of the tutorial\n.",
"title": "1 - Serialization"
- },
+ },
{
- "location": "/tutorial/1-serialization/#tutorial-1-serialization",
- "text": "",
+ "location": "/tutorial/1-serialization/#tutorial-1-serialization",
+ "text": "",
"title": "Tutorial 1: Serialization"
- },
+ },
{
- "location": "/tutorial/1-serialization/#introduction",
- "text": "This tutorial will cover creating a simple pastebin code highlighting Web API. Along the way it will introduce the various components that make up REST framework, and give you a comprehensive understanding of how everything fits together. The tutorial is fairly in-depth, so you should probably get a cookie and a cup of your favorite brew before getting started. If you just want a quick overview, you should head over to the quickstart documentation instead. Note : The code for this tutorial is available in the tomchristie/rest-framework-tutorial repository on GitHub. The completed implementation is also online as a sandbox version for testing, available here .",
+ "location": "/tutorial/1-serialization/#introduction",
+ "text": "This tutorial will cover creating a simple pastebin code highlighting Web API. Along the way it will introduce the various components that make up REST framework, and give you a comprehensive understanding of how everything fits together. The tutorial is fairly in-depth, so you should probably get a cookie and a cup of your favorite brew before getting started. If you just want a quick overview, you should head over to the quickstart documentation instead. Note : The code for this tutorial is available in the encode/rest-framework-tutorial repository on GitHub. The completed implementation is also online as a sandbox version for testing, available here .",
"title": "Introduction"
- },
+ },
{
- "location": "/tutorial/1-serialization/#setting-up-a-new-environment",
- "text": "Before we do anything else we'll create a new virtual environment, using virtualenv . This will make sure our package configuration is kept nicely isolated from any other projects we're working on. virtualenv env\nsource env/bin/activate Now that we're inside a virtualenv environment, we can install our package requirements. pip install django\npip install djangorestframework\npip install pygments # We'll be using this for the code highlighting Note: To exit the virtualenv environment at any time, just type deactivate . For more information see the virtualenv documentation .",
+ "location": "/tutorial/1-serialization/#setting-up-a-new-environment",
+ "text": "Before we do anything else we'll create a new virtual environment, using virtualenv . This will make sure our package configuration is kept nicely isolated from any other projects we're working on. virtualenv env\nsource env/bin/activate Now that we're inside a virtualenv environment, we can install our package requirements. pip install django\npip install djangorestframework\npip install pygments # We'll be using this for the code highlighting Note: To exit the virtualenv environment at any time, just type deactivate . For more information see the virtualenv documentation .",
"title": "Setting up a new environment"
- },
+ },
{
- "location": "/tutorial/1-serialization/#getting-started",
- "text": "Okay, we're ready to get coding.\nTo get started, let's create a new project to work with. cd ~\ndjango-admin startproject tutorial\ncd tutorial Once that's done we can create an app that we'll use to create a simple Web API. python manage.py startapp snippets We'll need to add our new snippets app and the rest_framework app to INSTALLED_APPS . Let's edit the tutorial/settings.py file: INSTALLED_APPS = (\n ...\n 'rest_framework',\n 'snippets.apps.SnippetsConfig',\n) Okay, we're ready to roll.",
+ "location": "/tutorial/1-serialization/#getting-started",
+ "text": "Okay, we're ready to get coding.\nTo get started, let's create a new project to work with. cd ~\ndjango-admin startproject tutorial\ncd tutorial Once that's done we can create an app that we'll use to create a simple Web API. python manage.py startapp snippets We'll need to add our new snippets app and the rest_framework app to INSTALLED_APPS . Let's edit the tutorial/settings.py file: INSTALLED_APPS = (\n ...\n 'rest_framework',\n 'snippets.apps.SnippetsConfig',\n) Okay, we're ready to roll.",
"title": "Getting started"
- },
+ },
{
- "location": "/tutorial/1-serialization/#creating-a-model-to-work-with",
- "text": "For the purposes of this tutorial we're going to start by creating a simple Snippet model that is used to store code snippets. Go ahead and edit the snippets/models.py file. Note: Good programming practices include comments. Although you will find them in our repository version of this tutorial code, we have omitted them here to focus on the code itself. from django.db import models\nfrom pygments.lexers import get_all_lexers\nfrom pygments.styles import get_all_styles\n\nLEXERS = [item for item in get_all_lexers() if item[1]]\nLANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])\nSTYLE_CHOICES = sorted((item, item) for item in get_all_styles())\n\n\nclass Snippet(models.Model):\n created = models.DateTimeField(auto_now_add=True)\n title = models.CharField(max_length=100, blank=True, default='')\n code = models.TextField()\n linenos = models.BooleanField(default=False)\n language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)\n style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)\n\n class Meta:\n ordering = ('created',) We'll also need to create an initial migration for our snippet model, and sync the database for the first time. python manage.py makemigrations snippets\npython manage.py migrate",
+ "location": "/tutorial/1-serialization/#creating-a-model-to-work-with",
+ "text": "For the purposes of this tutorial we're going to start by creating a simple Snippet model that is used to store code snippets. Go ahead and edit the snippets/models.py file. Note: Good programming practices include comments. Although you will find them in our repository version of this tutorial code, we have omitted them here to focus on the code itself. from django.db import models\nfrom pygments.lexers import get_all_lexers\nfrom pygments.styles import get_all_styles\n\nLEXERS = [item for item in get_all_lexers() if item[1]]\nLANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])\nSTYLE_CHOICES = sorted((item, item) for item in get_all_styles())\n\n\nclass Snippet(models.Model):\n created = models.DateTimeField(auto_now_add=True)\n title = models.CharField(max_length=100, blank=True, default='')\n code = models.TextField()\n linenos = models.BooleanField(default=False)\n language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)\n style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)\n\n class Meta:\n ordering = ('created',) We'll also need to create an initial migration for our snippet model, and sync the database for the first time. python manage.py makemigrations snippets\npython manage.py migrate",
"title": "Creating a model to work with"
- },
+ },
{
- "location": "/tutorial/1-serialization/#creating-a-serializer-class",
- "text": "The first thing we need to get started on our Web API is to provide a way of serializing and deserializing the snippet instances into representations such as json . We can do this by declaring serializers that work very similar to Django's forms. Create a file in the snippets directory named serializers.py and add the following. from rest_framework import serializers\nfrom snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES\n\n\nclass SnippetSerializer(serializers.Serializer):\n id = serializers.IntegerField(read_only=True)\n title = serializers.CharField(required=False, allow_blank=True, max_length=100)\n code = serializers.CharField(style={'base_template': 'textarea.html'})\n linenos = serializers.BooleanField(required=False)\n language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')\n style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')\n\n def create(self, validated_data):\n \"\"\"\n Create and return a new `Snippet` instance, given the validated data.\n \"\"\"\n return Snippet.objects.create(**validated_data)\n\n def update(self, instance, validated_data):\n \"\"\"\n Update and return an existing `Snippet` instance, given the validated data.\n \"\"\"\n instance.title = validated_data.get('title', instance.title)\n instance.code = validated_data.get('code', instance.code)\n instance.linenos = validated_data.get('linenos', instance.linenos)\n instance.language = validated_data.get('language', instance.language)\n instance.style = validated_data.get('style', instance.style)\n instance.save()\n return instance The first part of the serializer class defines the fields that get serialized/deserialized. The create() and update() methods define how fully fledged instances are created or modified when calling serializer.save() A serializer class is very similar to a Django Form class, and includes similar validation flags on the various fields, such as required , max_length and default . The field flags can also control how the serializer should be displayed in certain circumstances, such as when rendering to HTML. The {'base_template': 'textarea.html'} flag above is equivalent to using widget=widgets.Textarea on a Django Form class. This is particularly useful for controlling how the browsable API should be displayed, as we'll see later in the tutorial. We can actually also save ourselves some time by using the ModelSerializer class, as we'll see later, but for now we'll keep our serializer definition explicit.",
+ "location": "/tutorial/1-serialization/#creating-a-serializer-class",
+ "text": "The first thing we need to get started on our Web API is to provide a way of serializing and deserializing the snippet instances into representations such as json . We can do this by declaring serializers that work very similar to Django's forms. Create a file in the snippets directory named serializers.py and add the following. from rest_framework import serializers\nfrom snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES\n\n\nclass SnippetSerializer(serializers.Serializer):\n id = serializers.IntegerField(read_only=True)\n title = serializers.CharField(required=False, allow_blank=True, max_length=100)\n code = serializers.CharField(style={'base_template': 'textarea.html'})\n linenos = serializers.BooleanField(required=False)\n language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')\n style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')\n\n def create(self, validated_data):\n \"\"\"\n Create and return a new `Snippet` instance, given the validated data.\n \"\"\"\n return Snippet.objects.create(**validated_data)\n\n def update(self, instance, validated_data):\n \"\"\"\n Update and return an existing `Snippet` instance, given the validated data.\n \"\"\"\n instance.title = validated_data.get('title', instance.title)\n instance.code = validated_data.get('code', instance.code)\n instance.linenos = validated_data.get('linenos', instance.linenos)\n instance.language = validated_data.get('language', instance.language)\n instance.style = validated_data.get('style', instance.style)\n instance.save()\n return instance The first part of the serializer class defines the fields that get serialized/deserialized. The create() and update() methods define how fully fledged instances are created or modified when calling serializer.save() A serializer class is very similar to a Django Form class, and includes similar validation flags on the various fields, such as required , max_length and default . The field flags can also control how the serializer should be displayed in certain circumstances, such as when rendering to HTML. The {'base_template': 'textarea.html'} flag above is equivalent to using widget=widgets.Textarea on a Django Form class. This is particularly useful for controlling how the browsable API should be displayed, as we'll see later in the tutorial. We can actually also save ourselves some time by using the ModelSerializer class, as we'll see later, but for now we'll keep our serializer definition explicit.",
"title": "Creating a Serializer class"
- },
+ },
{
- "location": "/tutorial/1-serialization/#working-with-serializers",
- "text": "Before we go any further we'll familiarize ourselves with using our new Serializer class. Let's drop into the Django shell. python manage.py shell Okay, once we've got a few imports out of the way, let's create a couple of code snippets to work with. from snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\nfrom rest_framework.renderers import JSONRenderer\nfrom rest_framework.parsers import JSONParser\n\nsnippet = Snippet(code='foo = \"bar\"\\n')\nsnippet.save()\n\nsnippet = Snippet(code='print(\"hello, world\")\\n')\nsnippet.save() We've now got a few snippet instances to play with. Let's take a look at serializing one of those instances. serializer = SnippetSerializer(snippet)\nserializer.data\n# {'id': 2, 'title': '', 'code': 'print(\"hello, world\")\\n', 'linenos': False, 'language': 'python', 'style': 'friendly'} At this point we've translated the model instance into Python native datatypes. To finalize the serialization process we render the data into json . content = JSONRenderer().render(serializer.data)\ncontent\n# b'{\"id\": 2, \"title\": \"\", \"code\": \"print(\\\\\"hello, world\\\\\")\\\\n\", \"linenos\": false, \"language\": \"python\", \"style\": \"friendly\"}' Deserialization is similar. First we parse a stream into Python native datatypes... import io\n\nstream = io.BytesIO(content)\ndata = JSONParser().parse(stream) ...then we restore those native datatypes into a fully populated object instance. serializer = SnippetSerializer(data=data)\nserializer.is_valid()\n# True\nserializer.validated_data\n# OrderedDict([('title', ''), ('code', 'print(\"hello, world\")\\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])\nserializer.save()\n# Notice how similar the API is to working with forms. The similarity should become even more apparent when we start writing views that use our serializer. We can also serialize querysets instead of model instances. To do so we simply add a many=True flag to the serializer arguments. serializer = SnippetSerializer(Snippet.objects.all(), many=True)\nserializer.data\n# [OrderedDict([('id', 1), ('title', ''), ('code', 'foo = \"bar\"\\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', ''), ('code', 'print(\"hello, world\")\\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 3), ('title', ''), ('code', 'print(\"hello, world\")'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])]",
+ "location": "/tutorial/1-serialization/#working-with-serializers",
+ "text": "Before we go any further we'll familiarize ourselves with using our new Serializer class. Let's drop into the Django shell. python manage.py shell Okay, once we've got a few imports out of the way, let's create a couple of code snippets to work with. from snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\nfrom rest_framework.renderers import JSONRenderer\nfrom rest_framework.parsers import JSONParser\n\nsnippet = Snippet(code='foo = \"bar\"\\n')\nsnippet.save()\n\nsnippet = Snippet(code='print(\"hello, world\")\\n')\nsnippet.save() We've now got a few snippet instances to play with. Let's take a look at serializing one of those instances. serializer = SnippetSerializer(snippet)\nserializer.data\n# {'id': 2, 'title': '', 'code': 'print(\"hello, world\")\\n', 'linenos': False, 'language': 'python', 'style': 'friendly'} At this point we've translated the model instance into Python native datatypes. To finalize the serialization process we render the data into json . content = JSONRenderer().render(serializer.data)\ncontent\n# b'{\"id\": 2, \"title\": \"\", \"code\": \"print(\\\\\"hello, world\\\\\")\\\\n\", \"linenos\": false, \"language\": \"python\", \"style\": \"friendly\"}' Deserialization is similar. First we parse a stream into Python native datatypes... import io\n\nstream = io.BytesIO(content)\ndata = JSONParser().parse(stream) ...then we restore those native datatypes into a fully populated object instance. serializer = SnippetSerializer(data=data)\nserializer.is_valid()\n# True\nserializer.validated_data\n# OrderedDict([('title', ''), ('code', 'print(\"hello, world\")\\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])\nserializer.save()\n# Snippet: Snippet object Notice how similar the API is to working with forms. The similarity should become even more apparent when we start writing views that use our serializer. We can also serialize querysets instead of model instances. To do so we simply add a many=True flag to the serializer arguments. serializer = SnippetSerializer(Snippet.objects.all(), many=True)\nserializer.data\n# [OrderedDict([('id', 1), ('title', ''), ('code', 'foo = \"bar\"\\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', ''), ('code', 'print(\"hello, world\")\\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 3), ('title', ''), ('code', 'print(\"hello, world\")'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])]",
"title": "Working with Serializers"
- },
+ },
{
- "location": "/tutorial/1-serialization/#using-modelserializers",
- "text": "Our SnippetSerializer class is replicating a lot of information that's also contained in the Snippet model. It would be nice if we could keep our code a bit more concise. In the same way that Django provides both Form classes and ModelForm classes, REST framework includes both Serializer classes, and ModelSerializer classes. Let's look at refactoring our serializer using the ModelSerializer class.\nOpen the file snippets/serializers.py again, and replace the SnippetSerializer class with the following. class SnippetSerializer(serializers.ModelSerializer):\n class Meta:\n model = Snippet\n fields = ('id', 'title', 'code', 'linenos', 'language', 'style') One nice property that serializers have is that you can inspect all the fields in a serializer instance, by printing its representation. Open the Django shell with python manage.py shell , then try the following: from snippets.serializers import SnippetSerializer\nserializer = SnippetSerializer()\nprint(repr(serializer))\n# SnippetSerializer():\n# id = IntegerField(label='ID', read_only=True)\n# title = CharField(allow_blank=True, max_length=100, required=False)\n# code = CharField(style={'base_template': 'textarea.html'})\n# linenos = BooleanField(required=False)\n# language = ChoiceField(choices=[('Clipper', 'FoxPro'), ('Cucumber', 'Gherkin'), ('RobotFramework', 'RobotFramework'), ('abap', 'ABAP'), ('ada', 'Ada')...\n# style = ChoiceField(choices=[('autumn', 'autumn'), ('borland', 'borland'), ('bw', 'bw'), ('colorful', 'colorful')... It's important to remember that ModelSerializer classes don't do anything particularly magical, they are simply a shortcut for creating serializer classes: An automatically determined set of fields. Simple default implementations for the create() and update() methods.",
+ "location": "/tutorial/1-serialization/#using-modelserializers",
+ "text": "Our SnippetSerializer class is replicating a lot of information that's also contained in the Snippet model. It would be nice if we could keep our code a bit more concise. In the same way that Django provides both Form classes and ModelForm classes, REST framework includes both Serializer classes, and ModelSerializer classes. Let's look at refactoring our serializer using the ModelSerializer class.\nOpen the file snippets/serializers.py again, and replace the SnippetSerializer class with the following. class SnippetSerializer(serializers.ModelSerializer):\n class Meta:\n model = Snippet\n fields = ('id', 'title', 'code', 'linenos', 'language', 'style') One nice property that serializers have is that you can inspect all the fields in a serializer instance, by printing its representation. Open the Django shell with python manage.py shell , then try the following: from snippets.serializers import SnippetSerializer\nserializer = SnippetSerializer()\nprint(repr(serializer))\n# SnippetSerializer():\n# id = IntegerField(label='ID', read_only=True)\n# title = CharField(allow_blank=True, max_length=100, required=False)\n# code = CharField(style={'base_template': 'textarea.html'})\n# linenos = BooleanField(required=False)\n# language = ChoiceField(choices=[('Clipper', 'FoxPro'), ('Cucumber', 'Gherkin'), ('RobotFramework', 'RobotFramework'), ('abap', 'ABAP'), ('ada', 'Ada')...\n# style = ChoiceField(choices=[('autumn', 'autumn'), ('borland', 'borland'), ('bw', 'bw'), ('colorful', 'colorful')... It's important to remember that ModelSerializer classes don't do anything particularly magical, they are simply a shortcut for creating serializer classes: An automatically determined set of fields. Simple default implementations for the create() and update() methods.",
"title": "Using ModelSerializers"
- },
+ },
{
- "location": "/tutorial/1-serialization/#writing-regular-django-views-using-our-serializer",
- "text": "Let's see how we can write some API views using our new Serializer class.\nFor the moment we won't use any of REST framework's other features, we'll just write the views as regular Django views. Edit the snippets/views.py file, and add the following. from django.http import HttpResponse, JsonResponse\nfrom django.views.decorators.csrf import csrf_exempt\nfrom rest_framework.renderers import JSONRenderer\nfrom rest_framework.parsers import JSONParser\nfrom snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer The root of our API is going to be a view that supports listing all the existing snippets, or creating a new snippet. @csrf_exempt\ndef snippet_list(request):\n \"\"\"\n List all code snippets, or create a new snippet.\n \"\"\"\n if request.method == 'GET':\n snippets = Snippet.objects.all()\n serializer = SnippetSerializer(snippets, many=True)\n return JsonResponse(serializer.data, safe=False)\n\n elif request.method == 'POST':\n data = JSONParser().parse(request)\n serializer = SnippetSerializer(data=data)\n if serializer.is_valid():\n serializer.save()\n return JsonResponse(serializer.data, status=201)\n return JsonResponse(serializer.errors, status=400) Note that because we want to be able to POST to this view from clients that won't have a CSRF token we need to mark the view as csrf_exempt . This isn't something that you'd normally want to do, and REST framework views actually use more sensible behavior than this, but it'll do for our purposes right now. We'll also need a view which corresponds to an individual snippet, and can be used to retrieve, update or delete the snippet. @csrf_exempt\ndef snippet_detail(request, pk):\n \"\"\"\n Retrieve, update or delete a code snippet.\n \"\"\"\n try:\n snippet = Snippet.objects.get(pk=pk)\n except Snippet.DoesNotExist:\n return HttpResponse(status=404)\n\n if request.method == 'GET':\n serializer = SnippetSerializer(snippet)\n return JsonResponse(serializer.data)\n\n elif request.method == 'PUT':\n data = JSONParser().parse(request)\n serializer = SnippetSerializer(snippet, data=data)\n if serializer.is_valid():\n serializer.save()\n return JsonResponse(serializer.data)\n return JsonResponse(serializer.errors, status=400)\n\n elif request.method == 'DELETE':\n snippet.delete()\n return HttpResponse(status=204) Finally we need to wire these views up. Create the snippets/urls.py file: from django.urls import path\nfrom snippets import views\n\nurlpatterns = [\n path('snippets/', views.snippet_list),\n path('snippets//', views.snippet_detail),\n] We also need to wire up the root urlconf, in the tutorial/urls.py file, to include our snippet app's URLs. from django.urls import path, include\n\nurlpatterns = [\n path('', include('snippets.urls')),\n] It's worth noting that there are a couple of edge cases we're not dealing with properly at the moment. If we send malformed json , or if a request is made with a method that the view doesn't handle, then we'll end up with a 500 \"server error\" response. Still, this'll do for now.",
+ "location": "/tutorial/1-serialization/#writing-regular-django-views-using-our-serializer",
+ "text": "Let's see how we can write some API views using our new Serializer class.\nFor the moment we won't use any of REST framework's other features, we'll just write the views as regular Django views. Edit the snippets/views.py file, and add the following. from django.http import HttpResponse, JsonResponse\nfrom django.views.decorators.csrf import csrf_exempt\nfrom rest_framework.parsers import JSONParser\nfrom snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer The root of our API is going to be a view that supports listing all the existing snippets, or creating a new snippet. @csrf_exempt\ndef snippet_list(request):\n \"\"\"\n List all code snippets, or create a new snippet.\n \"\"\"\n if request.method == 'GET':\n snippets = Snippet.objects.all()\n serializer = SnippetSerializer(snippets, many=True)\n return JsonResponse(serializer.data, safe=False)\n\n elif request.method == 'POST':\n data = JSONParser().parse(request)\n serializer = SnippetSerializer(data=data)\n if serializer.is_valid():\n serializer.save()\n return JsonResponse(serializer.data, status=201)\n return JsonResponse(serializer.errors, status=400) Note that because we want to be able to POST to this view from clients that won't have a CSRF token we need to mark the view as csrf_exempt . This isn't something that you'd normally want to do, and REST framework views actually use more sensible behavior than this, but it'll do for our purposes right now. We'll also need a view which corresponds to an individual snippet, and can be used to retrieve, update or delete the snippet. @csrf_exempt\ndef snippet_detail(request, pk):\n \"\"\"\n Retrieve, update or delete a code snippet.\n \"\"\"\n try:\n snippet = Snippet.objects.get(pk=pk)\n except Snippet.DoesNotExist:\n return HttpResponse(status=404)\n\n if request.method == 'GET':\n serializer = SnippetSerializer(snippet)\n return JsonResponse(serializer.data)\n\n elif request.method == 'PUT':\n data = JSONParser().parse(request)\n serializer = SnippetSerializer(snippet, data=data)\n if serializer.is_valid():\n serializer.save()\n return JsonResponse(serializer.data)\n return JsonResponse(serializer.errors, status=400)\n\n elif request.method == 'DELETE':\n snippet.delete()\n return HttpResponse(status=204) Finally we need to wire these views up. Create the snippets/urls.py file: from django.urls import path\nfrom snippets import views\n\nurlpatterns = [\n path('snippets/', views.snippet_list),\n path('snippets/ int:pk /', views.snippet_detail),\n] We also need to wire up the root urlconf, in the tutorial/urls.py file, to include our snippet app's URLs. from django.urls import path, include\n\nurlpatterns = [\n path('', include('snippets.urls')),\n] It's worth noting that there are a couple of edge cases we're not dealing with properly at the moment. If we send malformed json , or if a request is made with a method that the view doesn't handle, then we'll end up with a 500 \"server error\" response. Still, this'll do for now.",
"title": "Writing regular Django views using our Serializer"
- },
+ },
{
- "location": "/tutorial/1-serialization/#testing-our-first-attempt-at-a-web-api",
- "text": "Now we can start up a sample server that serves our snippets. Quit out of the shell... quit() ...and start up Django's development server. python manage.py runserver\n\nValidating models...\n\n0 errors found\nDjango version 1.11, using settings 'tutorial.settings'\nDevelopment server is running at http://127.0.0.1:8000/\nQuit the server with CONTROL-C. In another terminal window, we can test the server. We can test our API using curl or httpie . Httpie is a user friendly http client that's written in Python. Let's install that. You can install httpie using pip: pip install httpie Finally, we can get a list of all of the snippets: http http://127.0.0.1:8000/snippets/\n\nHTTP/1.1 200 OK\n...\n[\n {\n \"id\": 1,\n \"title\": \"\",\n \"code\": \"foo = \\\"bar\\\"\\n\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n },\n {\n \"id\": 2,\n \"title\": \"\",\n \"code\": \"print(\\\"hello, world\\\")\\n\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n }\n] Or we can get a particular snippet by referencing its id: http http://127.0.0.1:8000/snippets/2/\n\nHTTP/1.1 200 OK\n...\n{\n \"id\": 2,\n \"title\": \"\",\n \"code\": \"print(\\\"hello, world\\\")\\n\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n} Similarly, you can have the same json displayed by visiting these URLs in a web browser.",
+ "location": "/tutorial/1-serialization/#testing-our-first-attempt-at-a-web-api",
+ "text": "Now we can start up a sample server that serves our snippets. Quit out of the shell... quit() ...and start up Django's development server. python manage.py runserver\n\nValidating models...\n\n0 errors found\nDjango version 1.11, using settings 'tutorial.settings'\nDevelopment server is running at http://127.0.0.1:8000/\nQuit the server with CONTROL-C. In another terminal window, we can test the server. We can test our API using curl or httpie . Httpie is a user friendly http client that's written in Python. Let's install that. You can install httpie using pip: pip install httpie Finally, we can get a list of all of the snippets: http http://127.0.0.1:8000/snippets/\n\nHTTP/1.1 200 OK\n...\n[\n {\n \"id\": 1,\n \"title\": \"\",\n \"code\": \"foo = \\\"bar\\\"\\n\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n },\n {\n \"id\": 2,\n \"title\": \"\",\n \"code\": \"print(\\\"hello, world\\\")\\n\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n }\n] Or we can get a particular snippet by referencing its id: http http://127.0.0.1:8000/snippets/2/\n\nHTTP/1.1 200 OK\n...\n{\n \"id\": 2,\n \"title\": \"\",\n \"code\": \"print(\\\"hello, world\\\")\\n\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n} Similarly, you can have the same json displayed by visiting these URLs in a web browser.",
"title": "Testing our first attempt at a Web API"
- },
+ },
{
- "location": "/tutorial/1-serialization/#where-are-we-now",
- "text": "We're doing okay so far, we've got a serialization API that feels pretty similar to Django's Forms API, and some regular Django views. Our API views don't do anything particularly special at the moment, beyond serving json responses, and there are some error handling edge cases we'd still like to clean up, but it's a functioning Web API. We'll see how we can start to improve things in part 2 of the tutorial .",
+ "location": "/tutorial/1-serialization/#where-are-we-now",
+ "text": "We're doing okay so far, we've got a serialization API that feels pretty similar to Django's Forms API, and some regular Django views. Our API views don't do anything particularly special at the moment, beyond serving json responses, and there are some error handling edge cases we'd still like to clean up, but it's a functioning Web API. We'll see how we can start to improve things in part 2 of the tutorial .",
"title": "Where are we now"
- },
+ },
{
- "location": "/tutorial/2-requests-and-responses/",
- "text": "Tutorial 2: Requests and Responses\n\n\nFrom this point we're going to really start covering the core of REST framework.\nLet's introduce a couple of essential building blocks.\n\n\nRequest objects\n\n\nREST framework introduces a \nRequest\n object that extends the regular \nHttpRequest\n, and provides more flexible request parsing. The core functionality of the \nRequest\n object is the \nrequest.data\n attribute, which is similar to \nrequest.POST\n, but more useful for working with Web APIs.\n\n\nrequest.POST # Only handles form data. Only works for 'POST' method.\nrequest.data # Handles arbitrary data. Works for 'POST', 'PUT' and 'PATCH' methods.\n\n\n\nResponse objects\n\n\nREST framework also introduces a \nResponse\n object, which is a type of \nTemplateResponse\n that takes unrendered content and uses content negotiation to determine the correct content type to return to the client.\n\n\nreturn Response(data) # Renders to content type as requested by the client.\n\n\n\nStatus codes\n\n\nUsing numeric HTTP status codes in your views doesn't always make for obvious reading, and it's easy to not notice if you get an error code wrong. REST framework provides more explicit identifiers for each status code, such as \nHTTP_400_BAD_REQUEST\n in the \nstatus\n module. It's a good idea to use these throughout rather than using numeric identifiers.\n\n\nWrapping API views\n\n\nREST framework provides two wrappers you can use to write API views.\n\n\n\n\nThe \n@api_view\n decorator for working with function based views.\n\n\nThe \nAPIView\n class for working with class-based views.\n\n\n\n\nThese wrappers provide a few bits of functionality such as making sure you receive \nRequest\n instances in your view, and adding context to \nResponse\n objects so that content negotiation can be performed.\n\n\nThe wrappers also provide behaviour such as returning \n405 Method Not Allowed\n responses when appropriate, and handling any \nParseError\n exception that occurs when accessing \nrequest.data\n with malformed input.\n\n\nPulling it all together\n\n\nOkay, let's go ahead and start using these new components to write a few views.\n\n\nWe don't need our \nJSONResponse\n class in \nviews.py\n any more, so go ahead and delete that. Once that's done we can start refactoring our views slightly.\n\n\nfrom rest_framework import status\nfrom rest_framework.decorators import api_view\nfrom rest_framework.response import Response\nfrom snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\n\n\n@api_view(['GET', 'POST'])\ndef snippet_list(request):\n \"\"\"\n List all code snippets, or create a new snippet.\n \"\"\"\n if request.method == 'GET':\n snippets = Snippet.objects.all()\n serializer = SnippetSerializer(snippets, many=True)\n return Response(serializer.data)\n\n elif request.method == 'POST':\n serializer = SnippetSerializer(data=request.data)\n if serializer.is_valid():\n serializer.save()\n return Response(serializer.data, status=status.HTTP_201_CREATED)\n return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n\n\n\nOur instance view is an improvement over the previous example. It's a little more concise, and the code now feels very similar to if we were working with the Forms API. We're also using named status codes, which makes the response meanings more obvious.\n\n\nHere is the view for an individual snippet, in the \nviews.py\n module.\n\n\n@api_view(['GET', 'PUT', 'DELETE'])\ndef snippet_detail(request, pk):\n \"\"\"\n Retrieve, update or delete a code snippet.\n \"\"\"\n try:\n snippet = Snippet.objects.get(pk=pk)\n except Snippet.DoesNotExist:\n return Response(status=status.HTTP_404_NOT_FOUND)\n\n if request.method == 'GET':\n serializer = SnippetSerializer(snippet)\n return Response(serializer.data)\n\n elif request.method == 'PUT':\n serializer = SnippetSerializer(snippet, data=request.data)\n if serializer.is_valid():\n serializer.save()\n return Response(serializer.data)\n return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n\n elif request.method == 'DELETE':\n snippet.delete()\n return Response(status=status.HTTP_204_NO_CONTENT)\n\n\n\nThis should all feel very familiar - it is not a lot different from working with regular Django views.\n\n\nNotice that we're no longer explicitly tying our requests or responses to a given content type. \nrequest.data\n can handle incoming \njson\n requests, but it can also handle other formats. Similarly we're returning response objects with data, but allowing REST framework to render the response into the correct content type for us.\n\n\nAdding optional format suffixes to our URLs\n\n\nTo take advantage of the fact that our responses are no longer hardwired to a single content type let's add support for format suffixes to our API endpoints. Using format suffixes gives us URLs that explicitly refer to a given format, and means our API will be able to handle URLs such as \nhttp://example.com/api/items/4.json\n.\n\n\nStart by adding a \nformat\n keyword argument to both of the views, like so.\n\n\ndef snippet_list(request, format=None):\n\n\n\nand\n\n\ndef snippet_detail(request, pk, format=None):\n\n\n\nNow update the \nsnippets/urls.py\n file slightly, to append a set of \nformat_suffix_patterns\n in addition to the existing URLs.\n\n\nfrom django.urls import path\nfrom rest_framework.urlpatterns import format_suffix_patterns\nfrom snippets import views\n\nurlpatterns = [\n path('snippets/', views.snippet_list),\n path('snippets/', views.snippet_detail),\n]\n\nurlpatterns = format_suffix_patterns(urlpatterns)\n\n\n\nWe don't necessarily need to add these extra url patterns in, but it gives us a simple, clean way of referring to a specific format.\n\n\nHow's it looking?\n\n\nGo ahead and test the API from the command line, as we did in \ntutorial part 1\n. Everything is working pretty similarly, although we've got some nicer error handling if we send invalid requests.\n\n\nWe can get a list of all of the snippets, as before.\n\n\nhttp http://127.0.0.1:8000/snippets/\n\nHTTP/1.1 200 OK\n...\n[\n {\n \"id\": 1,\n \"title\": \"\",\n \"code\": \"foo = \\\"bar\\\"\\n\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n },\n {\n \"id\": 2,\n \"title\": \"\",\n \"code\": \"print(\\\"hello, world\\\")\\n\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n }\n]\n\n\n\nWe can control the format of the response that we get back, either by using the \nAccept\n header:\n\n\nhttp http://127.0.0.1:8000/snippets/ Accept:application/json # Request JSON\nhttp http://127.0.0.1:8000/snippets/ Accept:text/html # Request HTML\n\n\n\nOr by appending a format suffix:\n\n\nhttp http://127.0.0.1:8000/snippets.json # JSON suffix\nhttp http://127.0.0.1:8000/snippets.api # Browsable API suffix\n\n\n\nSimilarly, we can control the format of the request that we send, using the \nContent-Type\n header.\n\n\n# POST using form data\nhttp --form POST http://127.0.0.1:8000/snippets/ code=\"print(123)\"\n\n{\n \"id\": 3,\n \"title\": \"\",\n \"code\": \"print(123)\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n}\n\n# POST using JSON\nhttp --json POST http://127.0.0.1:8000/snippets/ code=\"print(456)\"\n\n{\n \"id\": 4,\n \"title\": \"\",\n \"code\": \"print(456)\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n}\n\n\n\nIf you add a \n--debug\n switch to the \nhttp\n requests above, you will be able to see the request type in request headers.\n\n\nNow go and open the API in a web browser, by visiting \nhttp://127.0.0.1:8000/snippets/\n.\n\n\nBrowsability\n\n\nBecause the API chooses the content type of the response based on the client request, it will, by default, return an HTML-formatted representation of the resource when that resource is requested by a web browser. This allows for the API to return a fully web-browsable HTML representation.\n\n\nHaving a web-browsable API is a huge usability win, and makes developing and using your API much easier. It also dramatically lowers the barrier-to-entry for other developers wanting to inspect and work with your API.\n\n\nSee the \nbrowsable api\n topic for more information about the browsable API feature and how to customize it.\n\n\nWhat's next?\n\n\nIn \ntutorial part 3\n, we'll start using class-based views, and see how generic views reduce the amount of code we need to write.",
+ "location": "/tutorial/2-requests-and-responses/",
+ "text": "Tutorial 2: Requests and Responses\n\n\nFrom this point we're going to really start covering the core of REST framework.\nLet's introduce a couple of essential building blocks.\n\n\nRequest objects\n\n\nREST framework introduces a \nRequest\n object that extends the regular \nHttpRequest\n, and provides more flexible request parsing. The core functionality of the \nRequest\n object is the \nrequest.data\n attribute, which is similar to \nrequest.POST\n, but more useful for working with Web APIs.\n\n\nrequest.POST # Only handles form data. Only works for 'POST' method.\nrequest.data # Handles arbitrary data. Works for 'POST', 'PUT' and 'PATCH' methods.\n\n\n\nResponse objects\n\n\nREST framework also introduces a \nResponse\n object, which is a type of \nTemplateResponse\n that takes unrendered content and uses content negotiation to determine the correct content type to return to the client.\n\n\nreturn Response(data) # Renders to content type as requested by the client.\n\n\n\nStatus codes\n\n\nUsing numeric HTTP status codes in your views doesn't always make for obvious reading, and it's easy to not notice if you get an error code wrong. REST framework provides more explicit identifiers for each status code, such as \nHTTP_400_BAD_REQUEST\n in the \nstatus\n module. It's a good idea to use these throughout rather than using numeric identifiers.\n\n\nWrapping API views\n\n\nREST framework provides two wrappers you can use to write API views.\n\n\n\n\nThe \n@api_view\n decorator for working with function based views.\n\n\nThe \nAPIView\n class for working with class-based views.\n\n\n\n\nThese wrappers provide a few bits of functionality such as making sure you receive \nRequest\n instances in your view, and adding context to \nResponse\n objects so that content negotiation can be performed.\n\n\nThe wrappers also provide behaviour such as returning \n405 Method Not Allowed\n responses when appropriate, and handling any \nParseError\n exception that occurs when accessing \nrequest.data\n with malformed input.\n\n\nPulling it all together\n\n\nOkay, let's go ahead and start using these new components to write a few views.\n\n\nWe don't need our \nJSONResponse\n class in \nviews.py\n any more, so go ahead and delete that. Once that's done we can start refactoring our views slightly.\n\n\nfrom rest_framework import status\nfrom rest_framework.decorators import api_view\nfrom rest_framework.response import Response\nfrom snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\n\n\n@api_view(['GET', 'POST'])\ndef snippet_list(request):\n \"\"\"\n List all code snippets, or create a new snippet.\n \"\"\"\n if request.method == 'GET':\n snippets = Snippet.objects.all()\n serializer = SnippetSerializer(snippets, many=True)\n return Response(serializer.data)\n\n elif request.method == 'POST':\n serializer = SnippetSerializer(data=request.data)\n if serializer.is_valid():\n serializer.save()\n return Response(serializer.data, status=status.HTTP_201_CREATED)\n return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n\n\n\nOur instance view is an improvement over the previous example. It's a little more concise, and the code now feels very similar to if we were working with the Forms API. We're also using named status codes, which makes the response meanings more obvious.\n\n\nHere is the view for an individual snippet, in the \nviews.py\n module.\n\n\n@api_view(['GET', 'PUT', 'DELETE'])\ndef snippet_detail(request, pk):\n \"\"\"\n Retrieve, update or delete a code snippet.\n \"\"\"\n try:\n snippet = Snippet.objects.get(pk=pk)\n except Snippet.DoesNotExist:\n return Response(status=status.HTTP_404_NOT_FOUND)\n\n if request.method == 'GET':\n serializer = SnippetSerializer(snippet)\n return Response(serializer.data)\n\n elif request.method == 'PUT':\n serializer = SnippetSerializer(snippet, data=request.data)\n if serializer.is_valid():\n serializer.save()\n return Response(serializer.data)\n return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n\n elif request.method == 'DELETE':\n snippet.delete()\n return Response(status=status.HTTP_204_NO_CONTENT)\n\n\n\nThis should all feel very familiar - it is not a lot different from working with regular Django views.\n\n\nNotice that we're no longer explicitly tying our requests or responses to a given content type. \nrequest.data\n can handle incoming \njson\n requests, but it can also handle other formats. Similarly we're returning response objects with data, but allowing REST framework to render the response into the correct content type for us.\n\n\nAdding optional format suffixes to our URLs\n\n\nTo take advantage of the fact that our responses are no longer hardwired to a single content type let's add support for format suffixes to our API endpoints. Using format suffixes gives us URLs that explicitly refer to a given format, and means our API will be able to handle URLs such as \nhttp://example.com/api/items/4.json\n.\n\n\nStart by adding a \nformat\n keyword argument to both of the views, like so.\n\n\ndef snippet_list(request, format=None):\n\n\n\nand\n\n\ndef snippet_detail(request, pk, format=None):\n\n\n\nNow update the \nsnippets/urls.py\n file slightly, to append a set of \nformat_suffix_patterns\n in addition to the existing URLs.\n\n\nfrom django.urls import path\nfrom rest_framework.urlpatterns import format_suffix_patterns\nfrom snippets import views\n\nurlpatterns = [\n path('snippets/', views.snippet_list),\n path('snippets/\nint:pk\n', views.snippet_detail),\n]\n\nurlpatterns = format_suffix_patterns(urlpatterns)\n\n\n\nWe don't necessarily need to add these extra url patterns in, but it gives us a simple, clean way of referring to a specific format.\n\n\nHow's it looking?\n\n\nGo ahead and test the API from the command line, as we did in \ntutorial part 1\n. Everything is working pretty similarly, although we've got some nicer error handling if we send invalid requests.\n\n\nWe can get a list of all of the snippets, as before.\n\n\nhttp http://127.0.0.1:8000/snippets/\n\nHTTP/1.1 200 OK\n...\n[\n {\n \"id\": 1,\n \"title\": \"\",\n \"code\": \"foo = \\\"bar\\\"\\n\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n },\n {\n \"id\": 2,\n \"title\": \"\",\n \"code\": \"print(\\\"hello, world\\\")\\n\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n }\n]\n\n\n\nWe can control the format of the response that we get back, either by using the \nAccept\n header:\n\n\nhttp http://127.0.0.1:8000/snippets/ Accept:application/json # Request JSON\nhttp http://127.0.0.1:8000/snippets/ Accept:text/html # Request HTML\n\n\n\nOr by appending a format suffix:\n\n\nhttp http://127.0.0.1:8000/snippets.json # JSON suffix\nhttp http://127.0.0.1:8000/snippets.api # Browsable API suffix\n\n\n\nSimilarly, we can control the format of the request that we send, using the \nContent-Type\n header.\n\n\n# POST using form data\nhttp --form POST http://127.0.0.1:8000/snippets/ code=\"print(123)\"\n\n{\n \"id\": 3,\n \"title\": \"\",\n \"code\": \"print(123)\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n}\n\n# POST using JSON\nhttp --json POST http://127.0.0.1:8000/snippets/ code=\"print(456)\"\n\n{\n \"id\": 4,\n \"title\": \"\",\n \"code\": \"print(456)\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n}\n\n\n\nIf you add a \n--debug\n switch to the \nhttp\n requests above, you will be able to see the request type in request headers.\n\n\nNow go and open the API in a web browser, by visiting \nhttp://127.0.0.1:8000/snippets/\n.\n\n\nBrowsability\n\n\nBecause the API chooses the content type of the response based on the client request, it will, by default, return an HTML-formatted representation of the resource when that resource is requested by a web browser. This allows for the API to return a fully web-browsable HTML representation.\n\n\nHaving a web-browsable API is a huge usability win, and makes developing and using your API much easier. It also dramatically lowers the barrier-to-entry for other developers wanting to inspect and work with your API.\n\n\nSee the \nbrowsable api\n topic for more information about the browsable API feature and how to customize it.\n\n\nWhat's next?\n\n\nIn \ntutorial part 3\n, we'll start using class-based views, and see how generic views reduce the amount of code we need to write.",
"title": "2 - Requests and responses"
- },
+ },
{
- "location": "/tutorial/2-requests-and-responses/#tutorial-2-requests-and-responses",
- "text": "From this point we're going to really start covering the core of REST framework.\nLet's introduce a couple of essential building blocks.",
+ "location": "/tutorial/2-requests-and-responses/#tutorial-2-requests-and-responses",
+ "text": "From this point we're going to really start covering the core of REST framework.\nLet's introduce a couple of essential building blocks.",
"title": "Tutorial 2: Requests and Responses"
- },
+ },
{
- "location": "/tutorial/2-requests-and-responses/#request-objects",
- "text": "REST framework introduces a Request object that extends the regular HttpRequest , and provides more flexible request parsing. The core functionality of the Request object is the request.data attribute, which is similar to request.POST , but more useful for working with Web APIs. request.POST # Only handles form data. Only works for 'POST' method.\nrequest.data # Handles arbitrary data. Works for 'POST', 'PUT' and 'PATCH' methods.",
+ "location": "/tutorial/2-requests-and-responses/#request-objects",
+ "text": "REST framework introduces a Request object that extends the regular HttpRequest , and provides more flexible request parsing. The core functionality of the Request object is the request.data attribute, which is similar to request.POST , but more useful for working with Web APIs. request.POST # Only handles form data. Only works for 'POST' method.\nrequest.data # Handles arbitrary data. Works for 'POST', 'PUT' and 'PATCH' methods.",
"title": "Request objects"
- },
+ },
{
- "location": "/tutorial/2-requests-and-responses/#response-objects",
- "text": "REST framework also introduces a Response object, which is a type of TemplateResponse that takes unrendered content and uses content negotiation to determine the correct content type to return to the client. return Response(data) # Renders to content type as requested by the client.",
+ "location": "/tutorial/2-requests-and-responses/#response-objects",
+ "text": "REST framework also introduces a Response object, which is a type of TemplateResponse that takes unrendered content and uses content negotiation to determine the correct content type to return to the client. return Response(data) # Renders to content type as requested by the client.",
"title": "Response objects"
- },
+ },
{
- "location": "/tutorial/2-requests-and-responses/#status-codes",
- "text": "Using numeric HTTP status codes in your views doesn't always make for obvious reading, and it's easy to not notice if you get an error code wrong. REST framework provides more explicit identifiers for each status code, such as HTTP_400_BAD_REQUEST in the status module. It's a good idea to use these throughout rather than using numeric identifiers.",
+ "location": "/tutorial/2-requests-and-responses/#status-codes",
+ "text": "Using numeric HTTP status codes in your views doesn't always make for obvious reading, and it's easy to not notice if you get an error code wrong. REST framework provides more explicit identifiers for each status code, such as HTTP_400_BAD_REQUEST in the status module. It's a good idea to use these throughout rather than using numeric identifiers.",
"title": "Status codes"
- },
+ },
{
- "location": "/tutorial/2-requests-and-responses/#wrapping-api-views",
- "text": "REST framework provides two wrappers you can use to write API views. The @api_view decorator for working with function based views. The APIView class for working with class-based views. These wrappers provide a few bits of functionality such as making sure you receive Request instances in your view, and adding context to Response objects so that content negotiation can be performed. The wrappers also provide behaviour such as returning 405 Method Not Allowed responses when appropriate, and handling any ParseError exception that occurs when accessing request.data with malformed input.",
+ "location": "/tutorial/2-requests-and-responses/#wrapping-api-views",
+ "text": "REST framework provides two wrappers you can use to write API views. The @api_view decorator for working with function based views. The APIView class for working with class-based views. These wrappers provide a few bits of functionality such as making sure you receive Request instances in your view, and adding context to Response objects so that content negotiation can be performed. The wrappers also provide behaviour such as returning 405 Method Not Allowed responses when appropriate, and handling any ParseError exception that occurs when accessing request.data with malformed input.",
"title": "Wrapping API views"
- },
+ },
{
- "location": "/tutorial/2-requests-and-responses/#pulling-it-all-together",
- "text": "Okay, let's go ahead and start using these new components to write a few views. We don't need our JSONResponse class in views.py any more, so go ahead and delete that. Once that's done we can start refactoring our views slightly. from rest_framework import status\nfrom rest_framework.decorators import api_view\nfrom rest_framework.response import Response\nfrom snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\n\n\n@api_view(['GET', 'POST'])\ndef snippet_list(request):\n \"\"\"\n List all code snippets, or create a new snippet.\n \"\"\"\n if request.method == 'GET':\n snippets = Snippet.objects.all()\n serializer = SnippetSerializer(snippets, many=True)\n return Response(serializer.data)\n\n elif request.method == 'POST':\n serializer = SnippetSerializer(data=request.data)\n if serializer.is_valid():\n serializer.save()\n return Response(serializer.data, status=status.HTTP_201_CREATED)\n return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) Our instance view is an improvement over the previous example. It's a little more concise, and the code now feels very similar to if we were working with the Forms API. We're also using named status codes, which makes the response meanings more obvious. Here is the view for an individual snippet, in the views.py module. @api_view(['GET', 'PUT', 'DELETE'])\ndef snippet_detail(request, pk):\n \"\"\"\n Retrieve, update or delete a code snippet.\n \"\"\"\n try:\n snippet = Snippet.objects.get(pk=pk)\n except Snippet.DoesNotExist:\n return Response(status=status.HTTP_404_NOT_FOUND)\n\n if request.method == 'GET':\n serializer = SnippetSerializer(snippet)\n return Response(serializer.data)\n\n elif request.method == 'PUT':\n serializer = SnippetSerializer(snippet, data=request.data)\n if serializer.is_valid():\n serializer.save()\n return Response(serializer.data)\n return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n\n elif request.method == 'DELETE':\n snippet.delete()\n return Response(status=status.HTTP_204_NO_CONTENT) This should all feel very familiar - it is not a lot different from working with regular Django views. Notice that we're no longer explicitly tying our requests or responses to a given content type. request.data can handle incoming json requests, but it can also handle other formats. Similarly we're returning response objects with data, but allowing REST framework to render the response into the correct content type for us.",
+ "location": "/tutorial/2-requests-and-responses/#pulling-it-all-together",
+ "text": "Okay, let's go ahead and start using these new components to write a few views. We don't need our JSONResponse class in views.py any more, so go ahead and delete that. Once that's done we can start refactoring our views slightly. from rest_framework import status\nfrom rest_framework.decorators import api_view\nfrom rest_framework.response import Response\nfrom snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\n\n\n@api_view(['GET', 'POST'])\ndef snippet_list(request):\n \"\"\"\n List all code snippets, or create a new snippet.\n \"\"\"\n if request.method == 'GET':\n snippets = Snippet.objects.all()\n serializer = SnippetSerializer(snippets, many=True)\n return Response(serializer.data)\n\n elif request.method == 'POST':\n serializer = SnippetSerializer(data=request.data)\n if serializer.is_valid():\n serializer.save()\n return Response(serializer.data, status=status.HTTP_201_CREATED)\n return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) Our instance view is an improvement over the previous example. It's a little more concise, and the code now feels very similar to if we were working with the Forms API. We're also using named status codes, which makes the response meanings more obvious. Here is the view for an individual snippet, in the views.py module. @api_view(['GET', 'PUT', 'DELETE'])\ndef snippet_detail(request, pk):\n \"\"\"\n Retrieve, update or delete a code snippet.\n \"\"\"\n try:\n snippet = Snippet.objects.get(pk=pk)\n except Snippet.DoesNotExist:\n return Response(status=status.HTTP_404_NOT_FOUND)\n\n if request.method == 'GET':\n serializer = SnippetSerializer(snippet)\n return Response(serializer.data)\n\n elif request.method == 'PUT':\n serializer = SnippetSerializer(snippet, data=request.data)\n if serializer.is_valid():\n serializer.save()\n return Response(serializer.data)\n return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n\n elif request.method == 'DELETE':\n snippet.delete()\n return Response(status=status.HTTP_204_NO_CONTENT) This should all feel very familiar - it is not a lot different from working with regular Django views. Notice that we're no longer explicitly tying our requests or responses to a given content type. request.data can handle incoming json requests, but it can also handle other formats. Similarly we're returning response objects with data, but allowing REST framework to render the response into the correct content type for us.",
"title": "Pulling it all together"
- },
+ },
{
- "location": "/tutorial/2-requests-and-responses/#adding-optional-format-suffixes-to-our-urls",
- "text": "To take advantage of the fact that our responses are no longer hardwired to a single content type let's add support for format suffixes to our API endpoints. Using format suffixes gives us URLs that explicitly refer to a given format, and means our API will be able to handle URLs such as http://example.com/api/items/4.json . Start by adding a format keyword argument to both of the views, like so. def snippet_list(request, format=None): and def snippet_detail(request, pk, format=None): Now update the snippets/urls.py file slightly, to append a set of format_suffix_patterns in addition to the existing URLs. from django.urls import path\nfrom rest_framework.urlpatterns import format_suffix_patterns\nfrom snippets import views\n\nurlpatterns = [\n path('snippets/', views.snippet_list),\n path('snippets/', views.snippet_detail),\n]\n\nurlpatterns = format_suffix_patterns(urlpatterns) We don't necessarily need to add these extra url patterns in, but it gives us a simple, clean way of referring to a specific format.",
+ "location": "/tutorial/2-requests-and-responses/#adding-optional-format-suffixes-to-our-urls",
+ "text": "To take advantage of the fact that our responses are no longer hardwired to a single content type let's add support for format suffixes to our API endpoints. Using format suffixes gives us URLs that explicitly refer to a given format, and means our API will be able to handle URLs such as http://example.com/api/items/4.json . Start by adding a format keyword argument to both of the views, like so. def snippet_list(request, format=None): and def snippet_detail(request, pk, format=None): Now update the snippets/urls.py file slightly, to append a set of format_suffix_patterns in addition to the existing URLs. from django.urls import path\nfrom rest_framework.urlpatterns import format_suffix_patterns\nfrom snippets import views\n\nurlpatterns = [\n path('snippets/', views.snippet_list),\n path('snippets/ int:pk ', views.snippet_detail),\n]\n\nurlpatterns = format_suffix_patterns(urlpatterns) We don't necessarily need to add these extra url patterns in, but it gives us a simple, clean way of referring to a specific format.",
"title": "Adding optional format suffixes to our URLs"
- },
+ },
{
- "location": "/tutorial/2-requests-and-responses/#hows-it-looking",
- "text": "Go ahead and test the API from the command line, as we did in tutorial part 1 . Everything is working pretty similarly, although we've got some nicer error handling if we send invalid requests. We can get a list of all of the snippets, as before. http http://127.0.0.1:8000/snippets/\n\nHTTP/1.1 200 OK\n...\n[\n {\n \"id\": 1,\n \"title\": \"\",\n \"code\": \"foo = \\\"bar\\\"\\n\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n },\n {\n \"id\": 2,\n \"title\": \"\",\n \"code\": \"print(\\\"hello, world\\\")\\n\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n }\n] We can control the format of the response that we get back, either by using the Accept header: http http://127.0.0.1:8000/snippets/ Accept:application/json # Request JSON\nhttp http://127.0.0.1:8000/snippets/ Accept:text/html # Request HTML Or by appending a format suffix: http http://127.0.0.1:8000/snippets.json # JSON suffix\nhttp http://127.0.0.1:8000/snippets.api # Browsable API suffix Similarly, we can control the format of the request that we send, using the Content-Type header. # POST using form data\nhttp --form POST http://127.0.0.1:8000/snippets/ code=\"print(123)\"\n\n{\n \"id\": 3,\n \"title\": \"\",\n \"code\": \"print(123)\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n}\n\n# POST using JSON\nhttp --json POST http://127.0.0.1:8000/snippets/ code=\"print(456)\"\n\n{\n \"id\": 4,\n \"title\": \"\",\n \"code\": \"print(456)\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n} If you add a --debug switch to the http requests above, you will be able to see the request type in request headers. Now go and open the API in a web browser, by visiting http://127.0.0.1:8000/snippets/ .",
+ "location": "/tutorial/2-requests-and-responses/#hows-it-looking",
+ "text": "Go ahead and test the API from the command line, as we did in tutorial part 1 . Everything is working pretty similarly, although we've got some nicer error handling if we send invalid requests. We can get a list of all of the snippets, as before. http http://127.0.0.1:8000/snippets/\n\nHTTP/1.1 200 OK\n...\n[\n {\n \"id\": 1,\n \"title\": \"\",\n \"code\": \"foo = \\\"bar\\\"\\n\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n },\n {\n \"id\": 2,\n \"title\": \"\",\n \"code\": \"print(\\\"hello, world\\\")\\n\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n }\n] We can control the format of the response that we get back, either by using the Accept header: http http://127.0.0.1:8000/snippets/ Accept:application/json # Request JSON\nhttp http://127.0.0.1:8000/snippets/ Accept:text/html # Request HTML Or by appending a format suffix: http http://127.0.0.1:8000/snippets.json # JSON suffix\nhttp http://127.0.0.1:8000/snippets.api # Browsable API suffix Similarly, we can control the format of the request that we send, using the Content-Type header. # POST using form data\nhttp --form POST http://127.0.0.1:8000/snippets/ code=\"print(123)\"\n\n{\n \"id\": 3,\n \"title\": \"\",\n \"code\": \"print(123)\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n}\n\n# POST using JSON\nhttp --json POST http://127.0.0.1:8000/snippets/ code=\"print(456)\"\n\n{\n \"id\": 4,\n \"title\": \"\",\n \"code\": \"print(456)\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n} If you add a --debug switch to the http requests above, you will be able to see the request type in request headers. Now go and open the API in a web browser, by visiting http://127.0.0.1:8000/snippets/ .",
"title": "How's it looking?"
- },
+ },
{
- "location": "/tutorial/2-requests-and-responses/#browsability",
- "text": "Because the API chooses the content type of the response based on the client request, it will, by default, return an HTML-formatted representation of the resource when that resource is requested by a web browser. This allows for the API to return a fully web-browsable HTML representation. Having a web-browsable API is a huge usability win, and makes developing and using your API much easier. It also dramatically lowers the barrier-to-entry for other developers wanting to inspect and work with your API. See the browsable api topic for more information about the browsable API feature and how to customize it.",
+ "location": "/tutorial/2-requests-and-responses/#browsability",
+ "text": "Because the API chooses the content type of the response based on the client request, it will, by default, return an HTML-formatted representation of the resource when that resource is requested by a web browser. This allows for the API to return a fully web-browsable HTML representation. Having a web-browsable API is a huge usability win, and makes developing and using your API much easier. It also dramatically lowers the barrier-to-entry for other developers wanting to inspect and work with your API. See the browsable api topic for more information about the browsable API feature and how to customize it.",
"title": "Browsability"
- },
+ },
{
- "location": "/tutorial/2-requests-and-responses/#whats-next",
- "text": "In tutorial part 3 , we'll start using class-based views, and see how generic views reduce the amount of code we need to write.",
+ "location": "/tutorial/2-requests-and-responses/#whats-next",
+ "text": "In tutorial part 3 , we'll start using class-based views, and see how generic views reduce the amount of code we need to write.",
"title": "What's next?"
- },
+ },
{
- "location": "/tutorial/3-class-based-views/",
- "text": "Tutorial 3: Class-based Views\n\n\nWe can also write our API views using class-based views, rather than function based views. As we'll see this is a powerful pattern that allows us to reuse common functionality, and helps us keep our code \nDRY\n.\n\n\nRewriting our API using class-based views\n\n\nWe'll start by rewriting the root view as a class-based view. All this involves is a little bit of refactoring of \nviews.py\n.\n\n\nfrom snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\nfrom django.http import Http404\nfrom rest_framework.views import APIView\nfrom rest_framework.response import Response\nfrom rest_framework import status\n\n\nclass SnippetList(APIView):\n \"\"\"\n List all snippets, or create a new snippet.\n \"\"\"\n def get(self, request, format=None):\n snippets = Snippet.objects.all()\n serializer = SnippetSerializer(snippets, many=True)\n return Response(serializer.data)\n\n def post(self, request, format=None):\n serializer = SnippetSerializer(data=request.data)\n if serializer.is_valid():\n serializer.save()\n return Response(serializer.data, status=status.HTTP_201_CREATED)\n return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n\n\n\nSo far, so good. It looks pretty similar to the previous case, but we've got better separation between the different HTTP methods. We'll also need to update the instance view in \nviews.py\n.\n\n\nclass SnippetDetail(APIView):\n \"\"\"\n Retrieve, update or delete a snippet instance.\n \"\"\"\n def get_object(self, pk):\n try:\n return Snippet.objects.get(pk=pk)\n except Snippet.DoesNotExist:\n raise Http404\n\n def get(self, request, pk, format=None):\n snippet = self.get_object(pk)\n serializer = SnippetSerializer(snippet)\n return Response(serializer.data)\n\n def put(self, request, pk, format=None):\n snippet = self.get_object(pk)\n serializer = SnippetSerializer(snippet, data=request.data)\n if serializer.is_valid():\n serializer.save()\n return Response(serializer.data)\n return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n\n def delete(self, request, pk, format=None):\n snippet = self.get_object(pk)\n snippet.delete()\n return Response(status=status.HTTP_204_NO_CONTENT)\n\n\n\nThat's looking good. Again, it's still pretty similar to the function based view right now.\n\n\nWe'll also need to refactor our \nsnippets/urls.py\n slightly now that we're using class-based views.\n\n\nfrom django.urls import path\nfrom rest_framework.urlpatterns import format_suffix_patterns\nfrom snippets import views\n\nurlpatterns = [\n path('snippets/', views.SnippetList.as_view()),\n path('snippets//', views.SnippetDetail.as_view()),\n]\n\nurlpatterns = format_suffix_patterns(urlpatterns)\n\n\n\nOkay, we're done. If you run the development server everything should be working just as before.\n\n\nUsing mixins\n\n\nOne of the big wins of using class-based views is that it allows us to easily compose reusable bits of behaviour.\n\n\nThe create/retrieve/update/delete operations that we've been using so far are going to be pretty similar for any model-backed API views we create. Those bits of common behaviour are implemented in REST framework's mixin classes.\n\n\nLet's take a look at how we can compose the views by using the mixin classes. Here's our \nviews.py\n module again.\n\n\nfrom snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\nfrom rest_framework import mixins\nfrom rest_framework import generics\n\nclass SnippetList(mixins.ListModelMixin,\n mixins.CreateModelMixin,\n generics.GenericAPIView):\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n\n def get(self, request, *args, **kwargs):\n return self.list(request, *args, **kwargs)\n\n def post(self, request, *args, **kwargs):\n return self.create(request, *args, **kwargs)\n\n\n\nWe'll take a moment to examine exactly what's happening here. We're building our view using \nGenericAPIView\n, and adding in \nListModelMixin\n and \nCreateModelMixin\n.\n\n\nThe base class provides the core functionality, and the mixin classes provide the \n.list()\n and \n.create()\n actions. We're then explicitly binding the \nget\n and \npost\n methods to the appropriate actions. Simple enough stuff so far.\n\n\nclass SnippetDetail(mixins.RetrieveModelMixin,\n mixins.UpdateModelMixin,\n mixins.DestroyModelMixin,\n generics.GenericAPIView):\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n\n def get(self, request, *args, **kwargs):\n return self.retrieve(request, *args, **kwargs)\n\n def put(self, request, *args, **kwargs):\n return self.update(request, *args, **kwargs)\n\n def delete(self, request, *args, **kwargs):\n return self.destroy(request, *args, **kwargs)\n\n\n\nPretty similar. Again we're using the \nGenericAPIView\n class to provide the core functionality, and adding in mixins to provide the \n.retrieve()\n, \n.update()\n and \n.destroy()\n actions.\n\n\nUsing generic class-based views\n\n\nUsing the mixin classes we've rewritten the views to use slightly less code than before, but we can go one step further. REST framework provides a set of already mixed-in generic views that we can use to trim down our \nviews.py\n module even more.\n\n\nfrom snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\nfrom rest_framework import generics\n\n\nclass SnippetList(generics.ListCreateAPIView):\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n\n\nclass SnippetDetail(generics.RetrieveUpdateDestroyAPIView):\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n\n\n\nWow, that's pretty concise. We've gotten a huge amount for free, and our code looks like good, clean, idiomatic Django.\n\n\nNext we'll move onto \npart 4 of the tutorial\n, where we'll take a look at how we can deal with authentication and permissions for our API.",
+ "location": "/tutorial/3-class-based-views/",
+ "text": "Tutorial 3: Class-based Views\n\n\nWe can also write our API views using class-based views, rather than function based views. As we'll see this is a powerful pattern that allows us to reuse common functionality, and helps us keep our code \nDRY\n.\n\n\nRewriting our API using class-based views\n\n\nWe'll start by rewriting the root view as a class-based view. All this involves is a little bit of refactoring of \nviews.py\n.\n\n\nfrom snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\nfrom django.http import Http404\nfrom rest_framework.views import APIView\nfrom rest_framework.response import Response\nfrom rest_framework import status\n\n\nclass SnippetList(APIView):\n \"\"\"\n List all snippets, or create a new snippet.\n \"\"\"\n def get(self, request, format=None):\n snippets = Snippet.objects.all()\n serializer = SnippetSerializer(snippets, many=True)\n return Response(serializer.data)\n\n def post(self, request, format=None):\n serializer = SnippetSerializer(data=request.data)\n if serializer.is_valid():\n serializer.save()\n return Response(serializer.data, status=status.HTTP_201_CREATED)\n return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n\n\n\nSo far, so good. It looks pretty similar to the previous case, but we've got better separation between the different HTTP methods. We'll also need to update the instance view in \nviews.py\n.\n\n\nclass SnippetDetail(APIView):\n \"\"\"\n Retrieve, update or delete a snippet instance.\n \"\"\"\n def get_object(self, pk):\n try:\n return Snippet.objects.get(pk=pk)\n except Snippet.DoesNotExist:\n raise Http404\n\n def get(self, request, pk, format=None):\n snippet = self.get_object(pk)\n serializer = SnippetSerializer(snippet)\n return Response(serializer.data)\n\n def put(self, request, pk, format=None):\n snippet = self.get_object(pk)\n serializer = SnippetSerializer(snippet, data=request.data)\n if serializer.is_valid():\n serializer.save()\n return Response(serializer.data)\n return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n\n def delete(self, request, pk, format=None):\n snippet = self.get_object(pk)\n snippet.delete()\n return Response(status=status.HTTP_204_NO_CONTENT)\n\n\n\nThat's looking good. Again, it's still pretty similar to the function based view right now.\n\n\nWe'll also need to refactor our \nsnippets/urls.py\n slightly now that we're using class-based views.\n\n\nfrom django.urls import path\nfrom rest_framework.urlpatterns import format_suffix_patterns\nfrom snippets import views\n\nurlpatterns = [\n path('snippets/', views.SnippetList.as_view()),\n path('snippets/\nint:pk\n/', views.SnippetDetail.as_view()),\n]\n\nurlpatterns = format_suffix_patterns(urlpatterns)\n\n\n\nOkay, we're done. If you run the development server everything should be working just as before.\n\n\nUsing mixins\n\n\nOne of the big wins of using class-based views is that it allows us to easily compose reusable bits of behaviour.\n\n\nThe create/retrieve/update/delete operations that we've been using so far are going to be pretty similar for any model-backed API views we create. Those bits of common behaviour are implemented in REST framework's mixin classes.\n\n\nLet's take a look at how we can compose the views by using the mixin classes. Here's our \nviews.py\n module again.\n\n\nfrom snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\nfrom rest_framework import mixins\nfrom rest_framework import generics\n\nclass SnippetList(mixins.ListModelMixin,\n mixins.CreateModelMixin,\n generics.GenericAPIView):\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n\n def get(self, request, *args, **kwargs):\n return self.list(request, *args, **kwargs)\n\n def post(self, request, *args, **kwargs):\n return self.create(request, *args, **kwargs)\n\n\n\nWe'll take a moment to examine exactly what's happening here. We're building our view using \nGenericAPIView\n, and adding in \nListModelMixin\n and \nCreateModelMixin\n.\n\n\nThe base class provides the core functionality, and the mixin classes provide the \n.list()\n and \n.create()\n actions. We're then explicitly binding the \nget\n and \npost\n methods to the appropriate actions. Simple enough stuff so far.\n\n\nclass SnippetDetail(mixins.RetrieveModelMixin,\n mixins.UpdateModelMixin,\n mixins.DestroyModelMixin,\n generics.GenericAPIView):\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n\n def get(self, request, *args, **kwargs):\n return self.retrieve(request, *args, **kwargs)\n\n def put(self, request, *args, **kwargs):\n return self.update(request, *args, **kwargs)\n\n def delete(self, request, *args, **kwargs):\n return self.destroy(request, *args, **kwargs)\n\n\n\nPretty similar. Again we're using the \nGenericAPIView\n class to provide the core functionality, and adding in mixins to provide the \n.retrieve()\n, \n.update()\n and \n.destroy()\n actions.\n\n\nUsing generic class-based views\n\n\nUsing the mixin classes we've rewritten the views to use slightly less code than before, but we can go one step further. REST framework provides a set of already mixed-in generic views that we can use to trim down our \nviews.py\n module even more.\n\n\nfrom snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\nfrom rest_framework import generics\n\n\nclass SnippetList(generics.ListCreateAPIView):\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n\n\nclass SnippetDetail(generics.RetrieveUpdateDestroyAPIView):\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n\n\n\nWow, that's pretty concise. We've gotten a huge amount for free, and our code looks like good, clean, idiomatic Django.\n\n\nNext we'll move onto \npart 4 of the tutorial\n, where we'll take a look at how we can deal with authentication and permissions for our API.",
"title": "3 - Class based views"
- },
+ },
{
- "location": "/tutorial/3-class-based-views/#tutorial-3-class-based-views",
- "text": "We can also write our API views using class-based views, rather than function based views. As we'll see this is a powerful pattern that allows us to reuse common functionality, and helps us keep our code DRY .",
+ "location": "/tutorial/3-class-based-views/#tutorial-3-class-based-views",
+ "text": "We can also write our API views using class-based views, rather than function based views. As we'll see this is a powerful pattern that allows us to reuse common functionality, and helps us keep our code DRY .",
"title": "Tutorial 3: Class-based Views"
- },
+ },
{
- "location": "/tutorial/3-class-based-views/#rewriting-our-api-using-class-based-views",
- "text": "We'll start by rewriting the root view as a class-based view. All this involves is a little bit of refactoring of views.py . from snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\nfrom django.http import Http404\nfrom rest_framework.views import APIView\nfrom rest_framework.response import Response\nfrom rest_framework import status\n\n\nclass SnippetList(APIView):\n \"\"\"\n List all snippets, or create a new snippet.\n \"\"\"\n def get(self, request, format=None):\n snippets = Snippet.objects.all()\n serializer = SnippetSerializer(snippets, many=True)\n return Response(serializer.data)\n\n def post(self, request, format=None):\n serializer = SnippetSerializer(data=request.data)\n if serializer.is_valid():\n serializer.save()\n return Response(serializer.data, status=status.HTTP_201_CREATED)\n return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) So far, so good. It looks pretty similar to the previous case, but we've got better separation between the different HTTP methods. We'll also need to update the instance view in views.py . class SnippetDetail(APIView):\n \"\"\"\n Retrieve, update or delete a snippet instance.\n \"\"\"\n def get_object(self, pk):\n try:\n return Snippet.objects.get(pk=pk)\n except Snippet.DoesNotExist:\n raise Http404\n\n def get(self, request, pk, format=None):\n snippet = self.get_object(pk)\n serializer = SnippetSerializer(snippet)\n return Response(serializer.data)\n\n def put(self, request, pk, format=None):\n snippet = self.get_object(pk)\n serializer = SnippetSerializer(snippet, data=request.data)\n if serializer.is_valid():\n serializer.save()\n return Response(serializer.data)\n return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n\n def delete(self, request, pk, format=None):\n snippet = self.get_object(pk)\n snippet.delete()\n return Response(status=status.HTTP_204_NO_CONTENT) That's looking good. Again, it's still pretty similar to the function based view right now. We'll also need to refactor our snippets/urls.py slightly now that we're using class-based views. from django.urls import path\nfrom rest_framework.urlpatterns import format_suffix_patterns\nfrom snippets import views\n\nurlpatterns = [\n path('snippets/', views.SnippetList.as_view()),\n path('snippets//', views.SnippetDetail.as_view()),\n]\n\nurlpatterns = format_suffix_patterns(urlpatterns) Okay, we're done. If you run the development server everything should be working just as before.",
+ "location": "/tutorial/3-class-based-views/#rewriting-our-api-using-class-based-views",
+ "text": "We'll start by rewriting the root view as a class-based view. All this involves is a little bit of refactoring of views.py . from snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\nfrom django.http import Http404\nfrom rest_framework.views import APIView\nfrom rest_framework.response import Response\nfrom rest_framework import status\n\n\nclass SnippetList(APIView):\n \"\"\"\n List all snippets, or create a new snippet.\n \"\"\"\n def get(self, request, format=None):\n snippets = Snippet.objects.all()\n serializer = SnippetSerializer(snippets, many=True)\n return Response(serializer.data)\n\n def post(self, request, format=None):\n serializer = SnippetSerializer(data=request.data)\n if serializer.is_valid():\n serializer.save()\n return Response(serializer.data, status=status.HTTP_201_CREATED)\n return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) So far, so good. It looks pretty similar to the previous case, but we've got better separation between the different HTTP methods. We'll also need to update the instance view in views.py . class SnippetDetail(APIView):\n \"\"\"\n Retrieve, update or delete a snippet instance.\n \"\"\"\n def get_object(self, pk):\n try:\n return Snippet.objects.get(pk=pk)\n except Snippet.DoesNotExist:\n raise Http404\n\n def get(self, request, pk, format=None):\n snippet = self.get_object(pk)\n serializer = SnippetSerializer(snippet)\n return Response(serializer.data)\n\n def put(self, request, pk, format=None):\n snippet = self.get_object(pk)\n serializer = SnippetSerializer(snippet, data=request.data)\n if serializer.is_valid():\n serializer.save()\n return Response(serializer.data)\n return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n\n def delete(self, request, pk, format=None):\n snippet = self.get_object(pk)\n snippet.delete()\n return Response(status=status.HTTP_204_NO_CONTENT) That's looking good. Again, it's still pretty similar to the function based view right now. We'll also need to refactor our snippets/urls.py slightly now that we're using class-based views. from django.urls import path\nfrom rest_framework.urlpatterns import format_suffix_patterns\nfrom snippets import views\n\nurlpatterns = [\n path('snippets/', views.SnippetList.as_view()),\n path('snippets/ int:pk /', views.SnippetDetail.as_view()),\n]\n\nurlpatterns = format_suffix_patterns(urlpatterns) Okay, we're done. If you run the development server everything should be working just as before.",
"title": "Rewriting our API using class-based views"
- },
+ },
{
- "location": "/tutorial/3-class-based-views/#using-mixins",
- "text": "One of the big wins of using class-based views is that it allows us to easily compose reusable bits of behaviour. The create/retrieve/update/delete operations that we've been using so far are going to be pretty similar for any model-backed API views we create. Those bits of common behaviour are implemented in REST framework's mixin classes. Let's take a look at how we can compose the views by using the mixin classes. Here's our views.py module again. from snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\nfrom rest_framework import mixins\nfrom rest_framework import generics\n\nclass SnippetList(mixins.ListModelMixin,\n mixins.CreateModelMixin,\n generics.GenericAPIView):\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n\n def get(self, request, *args, **kwargs):\n return self.list(request, *args, **kwargs)\n\n def post(self, request, *args, **kwargs):\n return self.create(request, *args, **kwargs) We'll take a moment to examine exactly what's happening here. We're building our view using GenericAPIView , and adding in ListModelMixin and CreateModelMixin . The base class provides the core functionality, and the mixin classes provide the .list() and .create() actions. We're then explicitly binding the get and post methods to the appropriate actions. Simple enough stuff so far. class SnippetDetail(mixins.RetrieveModelMixin,\n mixins.UpdateModelMixin,\n mixins.DestroyModelMixin,\n generics.GenericAPIView):\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n\n def get(self, request, *args, **kwargs):\n return self.retrieve(request, *args, **kwargs)\n\n def put(self, request, *args, **kwargs):\n return self.update(request, *args, **kwargs)\n\n def delete(self, request, *args, **kwargs):\n return self.destroy(request, *args, **kwargs) Pretty similar. Again we're using the GenericAPIView class to provide the core functionality, and adding in mixins to provide the .retrieve() , .update() and .destroy() actions.",
+ "location": "/tutorial/3-class-based-views/#using-mixins",
+ "text": "One of the big wins of using class-based views is that it allows us to easily compose reusable bits of behaviour. The create/retrieve/update/delete operations that we've been using so far are going to be pretty similar for any model-backed API views we create. Those bits of common behaviour are implemented in REST framework's mixin classes. Let's take a look at how we can compose the views by using the mixin classes. Here's our views.py module again. from snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\nfrom rest_framework import mixins\nfrom rest_framework import generics\n\nclass SnippetList(mixins.ListModelMixin,\n mixins.CreateModelMixin,\n generics.GenericAPIView):\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n\n def get(self, request, *args, **kwargs):\n return self.list(request, *args, **kwargs)\n\n def post(self, request, *args, **kwargs):\n return self.create(request, *args, **kwargs) We'll take a moment to examine exactly what's happening here. We're building our view using GenericAPIView , and adding in ListModelMixin and CreateModelMixin . The base class provides the core functionality, and the mixin classes provide the .list() and .create() actions. We're then explicitly binding the get and post methods to the appropriate actions. Simple enough stuff so far. class SnippetDetail(mixins.RetrieveModelMixin,\n mixins.UpdateModelMixin,\n mixins.DestroyModelMixin,\n generics.GenericAPIView):\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n\n def get(self, request, *args, **kwargs):\n return self.retrieve(request, *args, **kwargs)\n\n def put(self, request, *args, **kwargs):\n return self.update(request, *args, **kwargs)\n\n def delete(self, request, *args, **kwargs):\n return self.destroy(request, *args, **kwargs) Pretty similar. Again we're using the GenericAPIView class to provide the core functionality, and adding in mixins to provide the .retrieve() , .update() and .destroy() actions.",
"title": "Using mixins"
- },
+ },
{
- "location": "/tutorial/3-class-based-views/#using-generic-class-based-views",
- "text": "Using the mixin classes we've rewritten the views to use slightly less code than before, but we can go one step further. REST framework provides a set of already mixed-in generic views that we can use to trim down our views.py module even more. from snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\nfrom rest_framework import generics\n\n\nclass SnippetList(generics.ListCreateAPIView):\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n\n\nclass SnippetDetail(generics.RetrieveUpdateDestroyAPIView):\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer Wow, that's pretty concise. We've gotten a huge amount for free, and our code looks like good, clean, idiomatic Django. Next we'll move onto part 4 of the tutorial , where we'll take a look at how we can deal with authentication and permissions for our API.",
+ "location": "/tutorial/3-class-based-views/#using-generic-class-based-views",
+ "text": "Using the mixin classes we've rewritten the views to use slightly less code than before, but we can go one step further. REST framework provides a set of already mixed-in generic views that we can use to trim down our views.py module even more. from snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\nfrom rest_framework import generics\n\n\nclass SnippetList(generics.ListCreateAPIView):\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n\n\nclass SnippetDetail(generics.RetrieveUpdateDestroyAPIView):\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer Wow, that's pretty concise. We've gotten a huge amount for free, and our code looks like good, clean, idiomatic Django. Next we'll move onto part 4 of the tutorial , where we'll take a look at how we can deal with authentication and permissions for our API.",
"title": "Using generic class-based views"
- },
+ },
{
- "location": "/tutorial/4-authentication-and-permissions/",
- "text": "Tutorial 4: Authentication & Permissions\n\n\nCurrently our API doesn't have any restrictions on who can edit or delete code snippets. We'd like to have some more advanced behavior in order to make sure that:\n\n\n\n\nCode snippets are always associated with a creator.\n\n\nOnly authenticated users may create snippets.\n\n\nOnly the creator of a snippet may update or delete it.\n\n\nUnauthenticated requests should have full read-only access.\n\n\n\n\nAdding information to our model\n\n\nWe're going to make a couple of changes to our \nSnippet\n model class.\nFirst, let's add a couple of fields. One of those fields will be used to represent the user who created the code snippet. The other field will be used to store the highlighted HTML representation of the code.\n\n\nAdd the following two fields to the \nSnippet\n model in \nmodels.py\n.\n\n\nowner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)\nhighlighted = models.TextField()\n\n\n\nWe'd also need to make sure that when the model is saved, that we populate the highlighted field, using the \npygments\n code highlighting library.\n\n\nWe'll need some extra imports:\n\n\nfrom pygments.lexers import get_lexer_by_name\nfrom pygments.formatters.html import HtmlFormatter\nfrom pygments import highlight\n\n\n\nAnd now we can add a \n.save()\n method to our model class:\n\n\ndef save(self, *args, **kwargs):\n \"\"\"\n Use the `pygments` library to create a highlighted HTML\n representation of the code snippet.\n \"\"\"\n lexer = get_lexer_by_name(self.language)\n linenos = 'table' if self.linenos else False\n options = {'title': self.title} if self.title else {}\n formatter = HtmlFormatter(style=self.style, linenos=linenos,\n full=True, **options)\n self.highlighted = highlight(self.code, lexer, formatter)\n super(Snippet, self).save(*args, **kwargs)\n\n\n\nWhen that's all done we'll need to update our database tables.\nNormally we'd create a database migration in order to do that, but for the purposes of this tutorial, let's just delete the database and start again.\n\n\nrm -f db.sqlite3\nrm -r snippets/migrations\npython manage.py makemigrations snippets\npython manage.py migrate\n\n\n\nYou might also want to create a few different users, to use for testing the API. The quickest way to do this will be with the \ncreatesuperuser\n command.\n\n\npython manage.py createsuperuser\n\n\n\nAdding endpoints for our User models\n\n\nNow that we've got some users to work with, we'd better add representations of those users to our API. Creating a new serializer is easy. In \nserializers.py\n add:\n\n\nfrom django.contrib.auth.models import User\n\nclass UserSerializer(serializers.ModelSerializer):\n snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())\n\n class Meta:\n model = User\n fields = ('id', 'username', 'snippets')\n\n\n\nBecause \n'snippets'\n is a \nreverse\n relationship on the User model, it will not be included by default when using the \nModelSerializer\n class, so we needed to add an explicit field for it.\n\n\nWe'll also add a couple of views to \nviews.py\n. We'd like to just use read-only views for the user representations, so we'll use the \nListAPIView\n and \nRetrieveAPIView\n generic class-based views.\n\n\nfrom django.contrib.auth.models import User\n\n\nclass UserList(generics.ListAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n\nclass UserDetail(generics.RetrieveAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n\n\nMake sure to also import the \nUserSerializer\n class\n\n\nfrom snippets.serializers import UserSerializer\n\n\n\nFinally we need to add those views into the API, by referencing them from the URL conf. Add the following to the patterns in \nsnippets/urls.py\n.\n\n\npath('users/', views.UserList.as_view()),\npath('users//', views.UserDetail.as_view()),\n\n\n\nAssociating Snippets with Users\n\n\nRight now, if we created a code snippet, there'd be no way of associating the user that created the snippet, with the snippet instance. The user isn't sent as part of the serialized representation, but is instead a property of the incoming request.\n\n\nThe way we deal with that is by overriding a \n.perform_create()\n method on our snippet views, that allows us to modify how the instance save is managed, and handle any information that is implicit in the incoming request or requested URL.\n\n\nOn the \nSnippetList\n view class, add the following method:\n\n\ndef perform_create(self, serializer):\n serializer.save(owner=self.request.user)\n\n\n\nThe \ncreate()\n method of our serializer will now be passed an additional \n'owner'\n field, along with the validated data from the request.\n\n\nUpdating our serializer\n\n\nNow that snippets are associated with the user that created them, let's update our \nSnippetSerializer\n to reflect that. Add the following field to the serializer definition in \nserializers.py\n:\n\n\nowner = serializers.ReadOnlyField(source='owner.username')\n\n\n\nNote\n: Make sure you also add \n'owner',\n to the list of fields in the inner \nMeta\n class.\n\n\nThis field is doing something quite interesting. The \nsource\n argument controls which attribute is used to populate a field, and can point at any attribute on the serialized instance. It can also take the dotted notation shown above, in which case it will traverse the given attributes, in a similar way as it is used with Django's template language.\n\n\nThe field we've added is the untyped \nReadOnlyField\n class, in contrast to the other typed fields, such as \nCharField\n, \nBooleanField\n etc... The untyped \nReadOnlyField\n is always read-only, and will be used for serialized representations, but will not be used for updating model instances when they are deserialized. We could have also used \nCharField(read_only=True)\n here.\n\n\nAdding required permissions to views\n\n\nNow that code snippets are associated with users, we want to make sure that only authenticated users are able to create, update and delete code snippets.\n\n\nREST framework includes a number of permission classes that we can use to restrict who can access a given view. In this case the one we're looking for is \nIsAuthenticatedOrReadOnly\n, which will ensure that authenticated requests get read-write access, and unauthenticated requests get read-only access.\n\n\nFirst add the following import in the views module\n\n\nfrom rest_framework import permissions\n\n\n\nThen, add the following property to \nboth\n the \nSnippetList\n and \nSnippetDetail\n view classes.\n\n\npermission_classes = (permissions.IsAuthenticatedOrReadOnly,)\n\n\n\nAdding login to the Browsable API\n\n\nIf you open a browser and navigate to the browsable API at the moment, you'll find that you're no longer able to create new code snippets. In order to do so we'd need to be able to login as a user.\n\n\nWe can add a login view for use with the browsable API, by editing the URLconf in our project-level \nurls.py\n file.\n\n\nAdd the following import at the top of the file:\n\n\nfrom django.conf.urls import include\n\n\n\nAnd, at the end of the file, add a pattern to include the login and logout views for the browsable API.\n\n\nurlpatterns += [\n path('api-auth/', include('rest_framework.urls')),\n]\n\n\n\nThe \n'api-auth/'\n part of pattern can actually be whatever URL you want to use.\n\n\nNow if you open up the browser again and refresh the page you'll see a 'Login' link in the top right of the page. If you log in as one of the users you created earlier, you'll be able to create code snippets again.\n\n\nOnce you've created a few code snippets, navigate to the '/users/' endpoint, and notice that the representation includes a list of the snippet ids that are associated with each user, in each user's 'snippets' field.\n\n\nObject level permissions\n\n\nReally we'd like all code snippets to be visible to anyone, but also make sure that only the user that created a code snippet is able to update or delete it.\n\n\nTo do that we're going to need to create a custom permission.\n\n\nIn the snippets app, create a new file, \npermissions.py\n\n\nfrom rest_framework import permissions\n\n\nclass IsOwnerOrReadOnly(permissions.BasePermission):\n \"\"\"\n Custom permission to only allow owners of an object to edit it.\n \"\"\"\n\n def has_object_permission(self, request, view, obj):\n # Read permissions are allowed to any request,\n # so we'll always allow GET, HEAD or OPTIONS requests.\n if request.method in permissions.SAFE_METHODS:\n return True\n\n # Write permissions are only allowed to the owner of the snippet.\n return obj.owner == request.user\n\n\n\nNow we can add that custom permission to our snippet instance endpoint, by editing the \npermission_classes\n property on the \nSnippetDetail\n view class:\n\n\npermission_classes = (permissions.IsAuthenticatedOrReadOnly,\n IsOwnerOrReadOnly,)\n\n\n\nMake sure to also import the \nIsOwnerOrReadOnly\n class.\n\n\nfrom snippets.permissions import IsOwnerOrReadOnly\n\n\n\nNow, if you open a browser again, you find that the 'DELETE' and 'PUT' actions only appear on a snippet instance endpoint if you're logged in as the same user that created the code snippet.\n\n\nAuthenticating with the API\n\n\nBecause we now have a set of permissions on the API, we need to authenticate our requests to it if we want to edit any snippets. We haven't set up any \nauthentication classes\n, so the defaults are currently applied, which are \nSessionAuthentication\n and \nBasicAuthentication\n.\n\n\nWhen we interact with the API through the web browser, we can login, and the browser session will then provide the required authentication for the requests.\n\n\nIf we're interacting with the API programmatically we need to explicitly provide the authentication credentials on each request.\n\n\nIf we try to create a snippet without authenticating, we'll get an error:\n\n\nhttp POST http://127.0.0.1:8000/snippets/ code=\"print(123)\"\n\n{\n \"detail\": \"Authentication credentials were not provided.\"\n}\n\n\n\nWe can make a successful request by including the username and password of one of the users we created earlier.\n\n\nhttp -a admin:password123 POST http://127.0.0.1:8000/snippets/ code=\"print(789)\"\n\n{\n \"id\": 1,\n \"owner\": \"admin\",\n \"title\": \"foo\",\n \"code\": \"print(789)\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n}\n\n\n\nSummary\n\n\nWe've now got a fairly fine-grained set of permissions on our Web API, and end points for users of the system and for the code snippets that they have created.\n\n\nIn \npart 5\n of the tutorial we'll look at how we can tie everything together by creating an HTML endpoint for our highlighted snippets, and improve the cohesion of our API by using hyperlinking for the relationships within the system.",
+ "location": "/tutorial/4-authentication-and-permissions/",
+ "text": "Tutorial 4: Authentication \n Permissions\n\n\nCurrently our API doesn't have any restrictions on who can edit or delete code snippets. We'd like to have some more advanced behavior in order to make sure that:\n\n\n\n\nCode snippets are always associated with a creator.\n\n\nOnly authenticated users may create snippets.\n\n\nOnly the creator of a snippet may update or delete it.\n\n\nUnauthenticated requests should have full read-only access.\n\n\n\n\nAdding information to our model\n\n\nWe're going to make a couple of changes to our \nSnippet\n model class.\nFirst, let's add a couple of fields. One of those fields will be used to represent the user who created the code snippet. The other field will be used to store the highlighted HTML representation of the code.\n\n\nAdd the following two fields to the \nSnippet\n model in \nmodels.py\n.\n\n\nowner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)\nhighlighted = models.TextField()\n\n\n\nWe'd also need to make sure that when the model is saved, that we populate the highlighted field, using the \npygments\n code highlighting library.\n\n\nWe'll need some extra imports:\n\n\nfrom pygments.lexers import get_lexer_by_name\nfrom pygments.formatters.html import HtmlFormatter\nfrom pygments import highlight\n\n\n\nAnd now we can add a \n.save()\n method to our model class:\n\n\ndef save(self, *args, **kwargs):\n \"\"\"\n Use the `pygments` library to create a highlighted HTML\n representation of the code snippet.\n \"\"\"\n lexer = get_lexer_by_name(self.language)\n linenos = 'table' if self.linenos else False\n options = {'title': self.title} if self.title else {}\n formatter = HtmlFormatter(style=self.style, linenos=linenos,\n full=True, **options)\n self.highlighted = highlight(self.code, lexer, formatter)\n super(Snippet, self).save(*args, **kwargs)\n\n\n\nWhen that's all done we'll need to update our database tables.\nNormally we'd create a database migration in order to do that, but for the purposes of this tutorial, let's just delete the database and start again.\n\n\nrm -f db.sqlite3\nrm -r snippets/migrations\npython manage.py makemigrations snippets\npython manage.py migrate\n\n\n\nYou might also want to create a few different users, to use for testing the API. The quickest way to do this will be with the \ncreatesuperuser\n command.\n\n\npython manage.py createsuperuser\n\n\n\nAdding endpoints for our User models\n\n\nNow that we've got some users to work with, we'd better add representations of those users to our API. Creating a new serializer is easy. In \nserializers.py\n add:\n\n\nfrom django.contrib.auth.models import User\n\nclass UserSerializer(serializers.ModelSerializer):\n snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())\n\n class Meta:\n model = User\n fields = ('id', 'username', 'snippets')\n\n\n\nBecause \n'snippets'\n is a \nreverse\n relationship on the User model, it will not be included by default when using the \nModelSerializer\n class, so we needed to add an explicit field for it.\n\n\nWe'll also add a couple of views to \nviews.py\n. We'd like to just use read-only views for the user representations, so we'll use the \nListAPIView\n and \nRetrieveAPIView\n generic class-based views.\n\n\nfrom django.contrib.auth.models import User\n\n\nclass UserList(generics.ListAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n\nclass UserDetail(generics.RetrieveAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n\n\nMake sure to also import the \nUserSerializer\n class\n\n\nfrom snippets.serializers import UserSerializer\n\n\n\nFinally we need to add those views into the API, by referencing them from the URL conf. Add the following to the patterns in \nsnippets/urls.py\n.\n\n\npath('users/', views.UserList.as_view()),\npath('users/\nint:pk\n/', views.UserDetail.as_view()),\n\n\n\nAssociating Snippets with Users\n\n\nRight now, if we created a code snippet, there'd be no way of associating the user that created the snippet, with the snippet instance. The user isn't sent as part of the serialized representation, but is instead a property of the incoming request.\n\n\nThe way we deal with that is by overriding a \n.perform_create()\n method on our snippet views, that allows us to modify how the instance save is managed, and handle any information that is implicit in the incoming request or requested URL.\n\n\nOn the \nSnippetList\n view class, add the following method:\n\n\ndef perform_create(self, serializer):\n serializer.save(owner=self.request.user)\n\n\n\nThe \ncreate()\n method of our serializer will now be passed an additional \n'owner'\n field, along with the validated data from the request.\n\n\nUpdating our serializer\n\n\nNow that snippets are associated with the user that created them, let's update our \nSnippetSerializer\n to reflect that. Add the following field to the serializer definition in \nserializers.py\n:\n\n\nowner = serializers.ReadOnlyField(source='owner.username')\n\n\n\nNote\n: Make sure you also add \n'owner',\n to the list of fields in the inner \nMeta\n class.\n\n\nThis field is doing something quite interesting. The \nsource\n argument controls which attribute is used to populate a field, and can point at any attribute on the serialized instance. It can also take the dotted notation shown above, in which case it will traverse the given attributes, in a similar way as it is used with Django's template language.\n\n\nThe field we've added is the untyped \nReadOnlyField\n class, in contrast to the other typed fields, such as \nCharField\n, \nBooleanField\n etc... The untyped \nReadOnlyField\n is always read-only, and will be used for serialized representations, but will not be used for updating model instances when they are deserialized. We could have also used \nCharField(read_only=True)\n here.\n\n\nAdding required permissions to views\n\n\nNow that code snippets are associated with users, we want to make sure that only authenticated users are able to create, update and delete code snippets.\n\n\nREST framework includes a number of permission classes that we can use to restrict who can access a given view. In this case the one we're looking for is \nIsAuthenticatedOrReadOnly\n, which will ensure that authenticated requests get read-write access, and unauthenticated requests get read-only access.\n\n\nFirst add the following import in the views module\n\n\nfrom rest_framework import permissions\n\n\n\nThen, add the following property to \nboth\n the \nSnippetList\n and \nSnippetDetail\n view classes.\n\n\npermission_classes = (permissions.IsAuthenticatedOrReadOnly,)\n\n\n\nAdding login to the Browsable API\n\n\nIf you open a browser and navigate to the browsable API at the moment, you'll find that you're no longer able to create new code snippets. In order to do so we'd need to be able to login as a user.\n\n\nWe can add a login view for use with the browsable API, by editing the URLconf in our project-level \nurls.py\n file.\n\n\nAdd the following import at the top of the file:\n\n\nfrom django.conf.urls import include\n\n\n\nAnd, at the end of the file, add a pattern to include the login and logout views for the browsable API.\n\n\nurlpatterns += [\n path('api-auth/', include('rest_framework.urls')),\n]\n\n\n\nThe \n'api-auth/'\n part of pattern can actually be whatever URL you want to use.\n\n\nNow if you open up the browser again and refresh the page you'll see a 'Login' link in the top right of the page. If you log in as one of the users you created earlier, you'll be able to create code snippets again.\n\n\nOnce you've created a few code snippets, navigate to the '/users/' endpoint, and notice that the representation includes a list of the snippet ids that are associated with each user, in each user's 'snippets' field.\n\n\nObject level permissions\n\n\nReally we'd like all code snippets to be visible to anyone, but also make sure that only the user that created a code snippet is able to update or delete it.\n\n\nTo do that we're going to need to create a custom permission.\n\n\nIn the snippets app, create a new file, \npermissions.py\n\n\nfrom rest_framework import permissions\n\n\nclass IsOwnerOrReadOnly(permissions.BasePermission):\n \"\"\"\n Custom permission to only allow owners of an object to edit it.\n \"\"\"\n\n def has_object_permission(self, request, view, obj):\n # Read permissions are allowed to any request,\n # so we'll always allow GET, HEAD or OPTIONS requests.\n if request.method in permissions.SAFE_METHODS:\n return True\n\n # Write permissions are only allowed to the owner of the snippet.\n return obj.owner == request.user\n\n\n\nNow we can add that custom permission to our snippet instance endpoint, by editing the \npermission_classes\n property on the \nSnippetDetail\n view class:\n\n\npermission_classes = (permissions.IsAuthenticatedOrReadOnly,\n IsOwnerOrReadOnly,)\n\n\n\nMake sure to also import the \nIsOwnerOrReadOnly\n class.\n\n\nfrom snippets.permissions import IsOwnerOrReadOnly\n\n\n\nNow, if you open a browser again, you find that the 'DELETE' and 'PUT' actions only appear on a snippet instance endpoint if you're logged in as the same user that created the code snippet.\n\n\nAuthenticating with the API\n\n\nBecause we now have a set of permissions on the API, we need to authenticate our requests to it if we want to edit any snippets. We haven't set up any \nauthentication classes\n, so the defaults are currently applied, which are \nSessionAuthentication\n and \nBasicAuthentication\n.\n\n\nWhen we interact with the API through the web browser, we can login, and the browser session will then provide the required authentication for the requests.\n\n\nIf we're interacting with the API programmatically we need to explicitly provide the authentication credentials on each request.\n\n\nIf we try to create a snippet without authenticating, we'll get an error:\n\n\nhttp POST http://127.0.0.1:8000/snippets/ code=\"print(123)\"\n\n{\n \"detail\": \"Authentication credentials were not provided.\"\n}\n\n\n\nWe can make a successful request by including the username and password of one of the users we created earlier.\n\n\nhttp -a admin:password123 POST http://127.0.0.1:8000/snippets/ code=\"print(789)\"\n\n{\n \"id\": 1,\n \"owner\": \"admin\",\n \"title\": \"foo\",\n \"code\": \"print(789)\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n}\n\n\n\nSummary\n\n\nWe've now got a fairly fine-grained set of permissions on our Web API, and end points for users of the system and for the code snippets that they have created.\n\n\nIn \npart 5\n of the tutorial we'll look at how we can tie everything together by creating an HTML endpoint for our highlighted snippets, and improve the cohesion of our API by using hyperlinking for the relationships within the system.",
"title": "4 - Authentication and permissions"
- },
+ },
{
- "location": "/tutorial/4-authentication-and-permissions/#tutorial-4-authentication-permissions",
- "text": "Currently our API doesn't have any restrictions on who can edit or delete code snippets. We'd like to have some more advanced behavior in order to make sure that: Code snippets are always associated with a creator. Only authenticated users may create snippets. Only the creator of a snippet may update or delete it. Unauthenticated requests should have full read-only access.",
+ "location": "/tutorial/4-authentication-and-permissions/#tutorial-4-authentication-permissions",
+ "text": "Currently our API doesn't have any restrictions on who can edit or delete code snippets. We'd like to have some more advanced behavior in order to make sure that: Code snippets are always associated with a creator. Only authenticated users may create snippets. Only the creator of a snippet may update or delete it. Unauthenticated requests should have full read-only access.",
"title": "Tutorial 4: Authentication & Permissions"
- },
+ },
{
- "location": "/tutorial/4-authentication-and-permissions/#adding-information-to-our-model",
- "text": "We're going to make a couple of changes to our Snippet model class.\nFirst, let's add a couple of fields. One of those fields will be used to represent the user who created the code snippet. The other field will be used to store the highlighted HTML representation of the code. Add the following two fields to the Snippet model in models.py . owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)\nhighlighted = models.TextField() We'd also need to make sure that when the model is saved, that we populate the highlighted field, using the pygments code highlighting library. We'll need some extra imports: from pygments.lexers import get_lexer_by_name\nfrom pygments.formatters.html import HtmlFormatter\nfrom pygments import highlight And now we can add a .save() method to our model class: def save(self, *args, **kwargs):\n \"\"\"\n Use the `pygments` library to create a highlighted HTML\n representation of the code snippet.\n \"\"\"\n lexer = get_lexer_by_name(self.language)\n linenos = 'table' if self.linenos else False\n options = {'title': self.title} if self.title else {}\n formatter = HtmlFormatter(style=self.style, linenos=linenos,\n full=True, **options)\n self.highlighted = highlight(self.code, lexer, formatter)\n super(Snippet, self).save(*args, **kwargs) When that's all done we'll need to update our database tables.\nNormally we'd create a database migration in order to do that, but for the purposes of this tutorial, let's just delete the database and start again. rm -f db.sqlite3\nrm -r snippets/migrations\npython manage.py makemigrations snippets\npython manage.py migrate You might also want to create a few different users, to use for testing the API. The quickest way to do this will be with the createsuperuser command. python manage.py createsuperuser",
+ "location": "/tutorial/4-authentication-and-permissions/#adding-information-to-our-model",
+ "text": "We're going to make a couple of changes to our Snippet model class.\nFirst, let's add a couple of fields. One of those fields will be used to represent the user who created the code snippet. The other field will be used to store the highlighted HTML representation of the code. Add the following two fields to the Snippet model in models.py . owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)\nhighlighted = models.TextField() We'd also need to make sure that when the model is saved, that we populate the highlighted field, using the pygments code highlighting library. We'll need some extra imports: from pygments.lexers import get_lexer_by_name\nfrom pygments.formatters.html import HtmlFormatter\nfrom pygments import highlight And now we can add a .save() method to our model class: def save(self, *args, **kwargs):\n \"\"\"\n Use the `pygments` library to create a highlighted HTML\n representation of the code snippet.\n \"\"\"\n lexer = get_lexer_by_name(self.language)\n linenos = 'table' if self.linenos else False\n options = {'title': self.title} if self.title else {}\n formatter = HtmlFormatter(style=self.style, linenos=linenos,\n full=True, **options)\n self.highlighted = highlight(self.code, lexer, formatter)\n super(Snippet, self).save(*args, **kwargs) When that's all done we'll need to update our database tables.\nNormally we'd create a database migration in order to do that, but for the purposes of this tutorial, let's just delete the database and start again. rm -f db.sqlite3\nrm -r snippets/migrations\npython manage.py makemigrations snippets\npython manage.py migrate You might also want to create a few different users, to use for testing the API. The quickest way to do this will be with the createsuperuser command. python manage.py createsuperuser",
"title": "Adding information to our model"
- },
+ },
{
- "location": "/tutorial/4-authentication-and-permissions/#adding-endpoints-for-our-user-models",
- "text": "Now that we've got some users to work with, we'd better add representations of those users to our API. Creating a new serializer is easy. In serializers.py add: from django.contrib.auth.models import User\n\nclass UserSerializer(serializers.ModelSerializer):\n snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())\n\n class Meta:\n model = User\n fields = ('id', 'username', 'snippets') Because 'snippets' is a reverse relationship on the User model, it will not be included by default when using the ModelSerializer class, so we needed to add an explicit field for it. We'll also add a couple of views to views.py . We'd like to just use read-only views for the user representations, so we'll use the ListAPIView and RetrieveAPIView generic class-based views. from django.contrib.auth.models import User\n\n\nclass UserList(generics.ListAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n\nclass UserDetail(generics.RetrieveAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer Make sure to also import the UserSerializer class from snippets.serializers import UserSerializer Finally we need to add those views into the API, by referencing them from the URL conf. Add the following to the patterns in snippets/urls.py . path('users/', views.UserList.as_view()),\npath('users//', views.UserDetail.as_view()),",
+ "location": "/tutorial/4-authentication-and-permissions/#adding-endpoints-for-our-user-models",
+ "text": "Now that we've got some users to work with, we'd better add representations of those users to our API. Creating a new serializer is easy. In serializers.py add: from django.contrib.auth.models import User\n\nclass UserSerializer(serializers.ModelSerializer):\n snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())\n\n class Meta:\n model = User\n fields = ('id', 'username', 'snippets') Because 'snippets' is a reverse relationship on the User model, it will not be included by default when using the ModelSerializer class, so we needed to add an explicit field for it. We'll also add a couple of views to views.py . We'd like to just use read-only views for the user representations, so we'll use the ListAPIView and RetrieveAPIView generic class-based views. from django.contrib.auth.models import User\n\n\nclass UserList(generics.ListAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n\nclass UserDetail(generics.RetrieveAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer Make sure to also import the UserSerializer class from snippets.serializers import UserSerializer Finally we need to add those views into the API, by referencing them from the URL conf. Add the following to the patterns in snippets/urls.py . path('users/', views.UserList.as_view()),\npath('users/ int:pk /', views.UserDetail.as_view()),",
"title": "Adding endpoints for our User models"
- },
+ },
{
- "location": "/tutorial/4-authentication-and-permissions/#associating-snippets-with-users",
- "text": "Right now, if we created a code snippet, there'd be no way of associating the user that created the snippet, with the snippet instance. The user isn't sent as part of the serialized representation, but is instead a property of the incoming request. The way we deal with that is by overriding a .perform_create() method on our snippet views, that allows us to modify how the instance save is managed, and handle any information that is implicit in the incoming request or requested URL. On the SnippetList view class, add the following method: def perform_create(self, serializer):\n serializer.save(owner=self.request.user) The create() method of our serializer will now be passed an additional 'owner' field, along with the validated data from the request.",
+ "location": "/tutorial/4-authentication-and-permissions/#associating-snippets-with-users",
+ "text": "Right now, if we created a code snippet, there'd be no way of associating the user that created the snippet, with the snippet instance. The user isn't sent as part of the serialized representation, but is instead a property of the incoming request. The way we deal with that is by overriding a .perform_create() method on our snippet views, that allows us to modify how the instance save is managed, and handle any information that is implicit in the incoming request or requested URL. On the SnippetList view class, add the following method: def perform_create(self, serializer):\n serializer.save(owner=self.request.user) The create() method of our serializer will now be passed an additional 'owner' field, along with the validated data from the request.",
"title": "Associating Snippets with Users"
- },
+ },
{
- "location": "/tutorial/4-authentication-and-permissions/#updating-our-serializer",
- "text": "Now that snippets are associated with the user that created them, let's update our SnippetSerializer to reflect that. Add the following field to the serializer definition in serializers.py : owner = serializers.ReadOnlyField(source='owner.username') Note : Make sure you also add 'owner', to the list of fields in the inner Meta class. This field is doing something quite interesting. The source argument controls which attribute is used to populate a field, and can point at any attribute on the serialized instance. It can also take the dotted notation shown above, in which case it will traverse the given attributes, in a similar way as it is used with Django's template language. The field we've added is the untyped ReadOnlyField class, in contrast to the other typed fields, such as CharField , BooleanField etc... The untyped ReadOnlyField is always read-only, and will be used for serialized representations, but will not be used for updating model instances when they are deserialized. We could have also used CharField(read_only=True) here.",
+ "location": "/tutorial/4-authentication-and-permissions/#updating-our-serializer",
+ "text": "Now that snippets are associated with the user that created them, let's update our SnippetSerializer to reflect that. Add the following field to the serializer definition in serializers.py : owner = serializers.ReadOnlyField(source='owner.username') Note : Make sure you also add 'owner', to the list of fields in the inner Meta class. This field is doing something quite interesting. The source argument controls which attribute is used to populate a field, and can point at any attribute on the serialized instance. It can also take the dotted notation shown above, in which case it will traverse the given attributes, in a similar way as it is used with Django's template language. The field we've added is the untyped ReadOnlyField class, in contrast to the other typed fields, such as CharField , BooleanField etc... The untyped ReadOnlyField is always read-only, and will be used for serialized representations, but will not be used for updating model instances when they are deserialized. We could have also used CharField(read_only=True) here.",
"title": "Updating our serializer"
- },
+ },
{
- "location": "/tutorial/4-authentication-and-permissions/#adding-required-permissions-to-views",
- "text": "Now that code snippets are associated with users, we want to make sure that only authenticated users are able to create, update and delete code snippets. REST framework includes a number of permission classes that we can use to restrict who can access a given view. In this case the one we're looking for is IsAuthenticatedOrReadOnly , which will ensure that authenticated requests get read-write access, and unauthenticated requests get read-only access. First add the following import in the views module from rest_framework import permissions Then, add the following property to both the SnippetList and SnippetDetail view classes. permission_classes = (permissions.IsAuthenticatedOrReadOnly,)",
+ "location": "/tutorial/4-authentication-and-permissions/#adding-required-permissions-to-views",
+ "text": "Now that code snippets are associated with users, we want to make sure that only authenticated users are able to create, update and delete code snippets. REST framework includes a number of permission classes that we can use to restrict who can access a given view. In this case the one we're looking for is IsAuthenticatedOrReadOnly , which will ensure that authenticated requests get read-write access, and unauthenticated requests get read-only access. First add the following import in the views module from rest_framework import permissions Then, add the following property to both the SnippetList and SnippetDetail view classes. permission_classes = (permissions.IsAuthenticatedOrReadOnly,)",
"title": "Adding required permissions to views"
- },
+ },
{
- "location": "/tutorial/4-authentication-and-permissions/#adding-login-to-the-browsable-api",
- "text": "If you open a browser and navigate to the browsable API at the moment, you'll find that you're no longer able to create new code snippets. In order to do so we'd need to be able to login as a user. We can add a login view for use with the browsable API, by editing the URLconf in our project-level urls.py file. Add the following import at the top of the file: from django.conf.urls import include And, at the end of the file, add a pattern to include the login and logout views for the browsable API. urlpatterns += [\n path('api-auth/', include('rest_framework.urls')),\n] The 'api-auth/' part of pattern can actually be whatever URL you want to use. Now if you open up the browser again and refresh the page you'll see a 'Login' link in the top right of the page. If you log in as one of the users you created earlier, you'll be able to create code snippets again. Once you've created a few code snippets, navigate to the '/users/' endpoint, and notice that the representation includes a list of the snippet ids that are associated with each user, in each user's 'snippets' field.",
+ "location": "/tutorial/4-authentication-and-permissions/#adding-login-to-the-browsable-api",
+ "text": "If you open a browser and navigate to the browsable API at the moment, you'll find that you're no longer able to create new code snippets. In order to do so we'd need to be able to login as a user. We can add a login view for use with the browsable API, by editing the URLconf in our project-level urls.py file. Add the following import at the top of the file: from django.conf.urls import include And, at the end of the file, add a pattern to include the login and logout views for the browsable API. urlpatterns += [\n path('api-auth/', include('rest_framework.urls')),\n] The 'api-auth/' part of pattern can actually be whatever URL you want to use. Now if you open up the browser again and refresh the page you'll see a 'Login' link in the top right of the page. If you log in as one of the users you created earlier, you'll be able to create code snippets again. Once you've created a few code snippets, navigate to the '/users/' endpoint, and notice that the representation includes a list of the snippet ids that are associated with each user, in each user's 'snippets' field.",
"title": "Adding login to the Browsable API"
- },
+ },
{
- "location": "/tutorial/4-authentication-and-permissions/#object-level-permissions",
- "text": "Really we'd like all code snippets to be visible to anyone, but also make sure that only the user that created a code snippet is able to update or delete it. To do that we're going to need to create a custom permission. In the snippets app, create a new file, permissions.py from rest_framework import permissions\n\n\nclass IsOwnerOrReadOnly(permissions.BasePermission):\n \"\"\"\n Custom permission to only allow owners of an object to edit it.\n \"\"\"\n\n def has_object_permission(self, request, view, obj):\n # Read permissions are allowed to any request,\n # so we'll always allow GET, HEAD or OPTIONS requests.\n if request.method in permissions.SAFE_METHODS:\n return True\n\n # Write permissions are only allowed to the owner of the snippet.\n return obj.owner == request.user Now we can add that custom permission to our snippet instance endpoint, by editing the permission_classes property on the SnippetDetail view class: permission_classes = (permissions.IsAuthenticatedOrReadOnly,\n IsOwnerOrReadOnly,) Make sure to also import the IsOwnerOrReadOnly class. from snippets.permissions import IsOwnerOrReadOnly Now, if you open a browser again, you find that the 'DELETE' and 'PUT' actions only appear on a snippet instance endpoint if you're logged in as the same user that created the code snippet.",
+ "location": "/tutorial/4-authentication-and-permissions/#object-level-permissions",
+ "text": "Really we'd like all code snippets to be visible to anyone, but also make sure that only the user that created a code snippet is able to update or delete it. To do that we're going to need to create a custom permission. In the snippets app, create a new file, permissions.py from rest_framework import permissions\n\n\nclass IsOwnerOrReadOnly(permissions.BasePermission):\n \"\"\"\n Custom permission to only allow owners of an object to edit it.\n \"\"\"\n\n def has_object_permission(self, request, view, obj):\n # Read permissions are allowed to any request,\n # so we'll always allow GET, HEAD or OPTIONS requests.\n if request.method in permissions.SAFE_METHODS:\n return True\n\n # Write permissions are only allowed to the owner of the snippet.\n return obj.owner == request.user Now we can add that custom permission to our snippet instance endpoint, by editing the permission_classes property on the SnippetDetail view class: permission_classes = (permissions.IsAuthenticatedOrReadOnly,\n IsOwnerOrReadOnly,) Make sure to also import the IsOwnerOrReadOnly class. from snippets.permissions import IsOwnerOrReadOnly Now, if you open a browser again, you find that the 'DELETE' and 'PUT' actions only appear on a snippet instance endpoint if you're logged in as the same user that created the code snippet.",
"title": "Object level permissions"
- },
+ },
{
- "location": "/tutorial/4-authentication-and-permissions/#authenticating-with-the-api",
- "text": "Because we now have a set of permissions on the API, we need to authenticate our requests to it if we want to edit any snippets. We haven't set up any authentication classes , so the defaults are currently applied, which are SessionAuthentication and BasicAuthentication . When we interact with the API through the web browser, we can login, and the browser session will then provide the required authentication for the requests. If we're interacting with the API programmatically we need to explicitly provide the authentication credentials on each request. If we try to create a snippet without authenticating, we'll get an error: http POST http://127.0.0.1:8000/snippets/ code=\"print(123)\"\n\n{\n \"detail\": \"Authentication credentials were not provided.\"\n} We can make a successful request by including the username and password of one of the users we created earlier. http -a admin:password123 POST http://127.0.0.1:8000/snippets/ code=\"print(789)\"\n\n{\n \"id\": 1,\n \"owner\": \"admin\",\n \"title\": \"foo\",\n \"code\": \"print(789)\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n}",
+ "location": "/tutorial/4-authentication-and-permissions/#authenticating-with-the-api",
+ "text": "Because we now have a set of permissions on the API, we need to authenticate our requests to it if we want to edit any snippets. We haven't set up any authentication classes , so the defaults are currently applied, which are SessionAuthentication and BasicAuthentication . When we interact with the API through the web browser, we can login, and the browser session will then provide the required authentication for the requests. If we're interacting with the API programmatically we need to explicitly provide the authentication credentials on each request. If we try to create a snippet without authenticating, we'll get an error: http POST http://127.0.0.1:8000/snippets/ code=\"print(123)\"\n\n{\n \"detail\": \"Authentication credentials were not provided.\"\n} We can make a successful request by including the username and password of one of the users we created earlier. http -a admin:password123 POST http://127.0.0.1:8000/snippets/ code=\"print(789)\"\n\n{\n \"id\": 1,\n \"owner\": \"admin\",\n \"title\": \"foo\",\n \"code\": \"print(789)\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n}",
"title": "Authenticating with the API"
- },
+ },
{
- "location": "/tutorial/4-authentication-and-permissions/#summary",
- "text": "We've now got a fairly fine-grained set of permissions on our Web API, and end points for users of the system and for the code snippets that they have created. In part 5 of the tutorial we'll look at how we can tie everything together by creating an HTML endpoint for our highlighted snippets, and improve the cohesion of our API by using hyperlinking for the relationships within the system.",
+ "location": "/tutorial/4-authentication-and-permissions/#summary",
+ "text": "We've now got a fairly fine-grained set of permissions on our Web API, and end points for users of the system and for the code snippets that they have created. In part 5 of the tutorial we'll look at how we can tie everything together by creating an HTML endpoint for our highlighted snippets, and improve the cohesion of our API by using hyperlinking for the relationships within the system.",
"title": "Summary"
- },
+ },
{
- "location": "/tutorial/5-relationships-and-hyperlinked-apis/",
- "text": "Tutorial 5: Relationships & Hyperlinked APIs\n\n\nAt the moment relationships within our API are represented by using primary keys. In this part of the tutorial we'll improve the cohesion and discoverability of our API, by instead using hyperlinking for relationships.\n\n\nCreating an endpoint for the root of our API\n\n\nRight now we have endpoints for 'snippets' and 'users', but we don't have a single entry point to our API. To create one, we'll use a regular function-based view and the \n@api_view\n decorator we introduced earlier. In your \nsnippets/views.py\n add:\n\n\nfrom rest_framework.decorators import api_view\nfrom rest_framework.response import Response\nfrom rest_framework.reverse import reverse\n\n\n@api_view(['GET'])\ndef api_root(request, format=None):\n return Response({\n 'users': reverse('user-list', request=request, format=format),\n 'snippets': reverse('snippet-list', request=request, format=format)\n })\n\n\n\nTwo things should be noticed here. First, we're using REST framework's \nreverse\n function in order to return fully-qualified URLs; second, URL patterns are identified by convenience names that we will declare later on in our \nsnippets/urls.py\n.\n\n\nCreating an endpoint for the highlighted snippets\n\n\nThe other obvious thing that's still missing from our pastebin API is the code highlighting endpoints.\n\n\nUnlike all our other API endpoints, we don't want to use JSON, but instead just present an HTML representation. There are two styles of HTML renderer provided by REST framework, one for dealing with HTML rendered using templates, the other for dealing with pre-rendered HTML. The second renderer is the one we'd like to use for this endpoint.\n\n\nThe other thing we need to consider when creating the code highlight view is that there's no existing concrete generic view that we can use. We're not returning an object instance, but instead a property of an object instance.\n\n\nInstead of using a concrete generic view, we'll use the base class for representing instances, and create our own \n.get()\n method. In your \nsnippets/views.py\n add:\n\n\nfrom rest_framework import renderers\nfrom rest_framework.response import Response\n\nclass SnippetHighlight(generics.GenericAPIView):\n queryset = Snippet.objects.all()\n renderer_classes = (renderers.StaticHTMLRenderer,)\n\n def get(self, request, *args, **kwargs):\n snippet = self.get_object()\n return Response(snippet.highlighted)\n\n\n\nAs usual we need to add the new views that we've created in to our URLconf.\nWe'll add a url pattern for our new API root in \nsnippets/urls.py\n:\n\n\npath('', views.api_root),\n\n\n\nAnd then add a url pattern for the snippet highlights:\n\n\npath('snippets//highlight/', views.SnippetHighlight.as_view()),\n\n\n\nHyperlinking our API\n\n\nDealing with relationships between entities is one of the more challenging aspects of Web API design. There are a number of different ways that we might choose to represent a relationship:\n\n\n\n\nUsing primary keys.\n\n\nUsing hyperlinking between entities.\n\n\nUsing a unique identifying slug field on the related entity.\n\n\nUsing the default string representation of the related entity.\n\n\nNesting the related entity inside the parent representation.\n\n\nSome other custom representation.\n\n\n\n\nREST framework supports all of these styles, and can apply them across forward or reverse relationships, or apply them across custom managers such as generic foreign keys.\n\n\nIn this case we'd like to use a hyperlinked style between entities. In order to do so, we'll modify our serializers to extend \nHyperlinkedModelSerializer\n instead of the existing \nModelSerializer\n.\n\n\nThe \nHyperlinkedModelSerializer\n has the following differences from \nModelSerializer\n:\n\n\n\n\nIt does not include the \nid\n field by default.\n\n\nIt includes a \nurl\n field, using \nHyperlinkedIdentityField\n.\n\n\nRelationships use \nHyperlinkedRelatedField\n,\n instead of \nPrimaryKeyRelatedField\n.\n\n\n\n\nWe can easily re-write our existing serializers to use hyperlinking. In your \nsnippets/serializers.py\n add:\n\n\nclass SnippetSerializer(serializers.HyperlinkedModelSerializer):\n owner = serializers.ReadOnlyField(source='owner.username')\n highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight', format='html')\n\n class Meta:\n model = Snippet\n fields = ('url', 'id', 'highlight', 'owner',\n 'title', 'code', 'linenos', 'language', 'style')\n\n\nclass UserSerializer(serializers.HyperlinkedModelSerializer):\n snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True)\n\n class Meta:\n model = User\n fields = ('url', 'id', 'username', 'snippets')\n\n\n\nNotice that we've also added a new \n'highlight'\n field. This field is of the same type as the \nurl\n field, except that it points to the \n'snippet-highlight'\n url pattern, instead of the \n'snippet-detail'\n url pattern.\n\n\nBecause we've included format suffixed URLs such as \n'.json'\n, we also need to indicate on the \nhighlight\n field that any format suffixed hyperlinks it returns should use the \n'.html'\n suffix.\n\n\nMaking sure our URL patterns are named\n\n\nIf we're going to have a hyperlinked API, we need to make sure we name our URL patterns. Let's take a look at which URL patterns we need to name.\n\n\n\n\nThe root of our API refers to \n'user-list'\n and \n'snippet-list'\n.\n\n\nOur snippet serializer includes a field that refers to \n'snippet-highlight'\n.\n\n\nOur user serializer includes a field that refers to \n'snippet-detail'\n.\n\n\nOur snippet and user serializers include \n'url'\n fields that by default will refer to \n'{model_name}-detail'\n, which in this case will be \n'snippet-detail'\n and \n'user-detail'\n.\n\n\n\n\nAfter adding all those names into our URLconf, our final \nsnippets/urls.py\n file should look like this:\n\n\nfrom django.urls import path\nfrom rest_framework.urlpatterns import format_suffix_patterns\nfrom snippets import views\n\n# API endpoints\nurlpatterns = format_suffix_patterns([\n path('', views.api_root),\n path('snippets/',\n views.SnippetList.as_view(),\n name='snippet-list'),\n path('snippets//',\n views.SnippetDetail.as_view(),\n name='snippet-detail'),\n path('snippets//highlight/',\n views.SnippetHighlight.as_view(),\n name='snippet-highlight'),\n path('users/',\n views.UserList.as_view(),\n name='user-list'),\n path('users//',\n views.UserDetail.as_view(),\n name='user-detail')\n])\n\n\n\nAdding pagination\n\n\nThe list views for users and code snippets could end up returning quite a lot of instances, so really we'd like to make sure we paginate the results, and allow the API client to step through each of the individual pages.\n\n\nWe can change the default list style to use pagination, by modifying our \ntutorial/settings.py\n file slightly. Add the following setting:\n\n\nREST_FRAMEWORK = {\n 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',\n 'PAGE_SIZE': 10\n}\n\n\n\nNote that settings in REST framework are all namespaced into a single dictionary setting, named \nREST_FRAMEWORK\n, which helps keep them well separated from your other project settings.\n\n\nWe could also customize the pagination style if we needed too, but in this case we'll just stick with the default.\n\n\nBrowsing the API\n\n\nIf we open a browser and navigate to the browsable API, you'll find that you can now work your way around the API simply by following links.\n\n\nYou'll also be able to see the 'highlight' links on the snippet instances, that will take you to the highlighted code HTML representations.\n\n\nIn \npart 6\n of the tutorial we'll look at how we can use ViewSets and Routers to reduce the amount of code we need to build our API.",
+ "location": "/tutorial/5-relationships-and-hyperlinked-apis/",
+ "text": "Tutorial 5: Relationships \n Hyperlinked APIs\n\n\nAt the moment relationships within our API are represented by using primary keys. In this part of the tutorial we'll improve the cohesion and discoverability of our API, by instead using hyperlinking for relationships.\n\n\nCreating an endpoint for the root of our API\n\n\nRight now we have endpoints for 'snippets' and 'users', but we don't have a single entry point to our API. To create one, we'll use a regular function-based view and the \n@api_view\n decorator we introduced earlier. In your \nsnippets/views.py\n add:\n\n\nfrom rest_framework.decorators import api_view\nfrom rest_framework.response import Response\nfrom rest_framework.reverse import reverse\n\n\n@api_view(['GET'])\ndef api_root(request, format=None):\n return Response({\n 'users': reverse('user-list', request=request, format=format),\n 'snippets': reverse('snippet-list', request=request, format=format)\n })\n\n\n\nTwo things should be noticed here. First, we're using REST framework's \nreverse\n function in order to return fully-qualified URLs; second, URL patterns are identified by convenience names that we will declare later on in our \nsnippets/urls.py\n.\n\n\nCreating an endpoint for the highlighted snippets\n\n\nThe other obvious thing that's still missing from our pastebin API is the code highlighting endpoints.\n\n\nUnlike all our other API endpoints, we don't want to use JSON, but instead just present an HTML representation. There are two styles of HTML renderer provided by REST framework, one for dealing with HTML rendered using templates, the other for dealing with pre-rendered HTML. The second renderer is the one we'd like to use for this endpoint.\n\n\nThe other thing we need to consider when creating the code highlight view is that there's no existing concrete generic view that we can use. We're not returning an object instance, but instead a property of an object instance.\n\n\nInstead of using a concrete generic view, we'll use the base class for representing instances, and create our own \n.get()\n method. In your \nsnippets/views.py\n add:\n\n\nfrom rest_framework import renderers\nfrom rest_framework.response import Response\n\nclass SnippetHighlight(generics.GenericAPIView):\n queryset = Snippet.objects.all()\n renderer_classes = (renderers.StaticHTMLRenderer,)\n\n def get(self, request, *args, **kwargs):\n snippet = self.get_object()\n return Response(snippet.highlighted)\n\n\n\nAs usual we need to add the new views that we've created in to our URLconf.\nWe'll add a url pattern for our new API root in \nsnippets/urls.py\n:\n\n\npath('', views.api_root),\n\n\n\nAnd then add a url pattern for the snippet highlights:\n\n\npath('snippets/\nint:pk\n/highlight/', views.SnippetHighlight.as_view()),\n\n\n\nHyperlinking our API\n\n\nDealing with relationships between entities is one of the more challenging aspects of Web API design. There are a number of different ways that we might choose to represent a relationship:\n\n\n\n\nUsing primary keys.\n\n\nUsing hyperlinking between entities.\n\n\nUsing a unique identifying slug field on the related entity.\n\n\nUsing the default string representation of the related entity.\n\n\nNesting the related entity inside the parent representation.\n\n\nSome other custom representation.\n\n\n\n\nREST framework supports all of these styles, and can apply them across forward or reverse relationships, or apply them across custom managers such as generic foreign keys.\n\n\nIn this case we'd like to use a hyperlinked style between entities. In order to do so, we'll modify our serializers to extend \nHyperlinkedModelSerializer\n instead of the existing \nModelSerializer\n.\n\n\nThe \nHyperlinkedModelSerializer\n has the following differences from \nModelSerializer\n:\n\n\n\n\nIt does not include the \nid\n field by default.\n\n\nIt includes a \nurl\n field, using \nHyperlinkedIdentityField\n.\n\n\nRelationships use \nHyperlinkedRelatedField\n,\n instead of \nPrimaryKeyRelatedField\n.\n\n\n\n\nWe can easily re-write our existing serializers to use hyperlinking. In your \nsnippets/serializers.py\n add:\n\n\nclass SnippetSerializer(serializers.HyperlinkedModelSerializer):\n owner = serializers.ReadOnlyField(source='owner.username')\n highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight', format='html')\n\n class Meta:\n model = Snippet\n fields = ('url', 'id', 'highlight', 'owner',\n 'title', 'code', 'linenos', 'language', 'style')\n\n\nclass UserSerializer(serializers.HyperlinkedModelSerializer):\n snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True)\n\n class Meta:\n model = User\n fields = ('url', 'id', 'username', 'snippets')\n\n\n\nNotice that we've also added a new \n'highlight'\n field. This field is of the same type as the \nurl\n field, except that it points to the \n'snippet-highlight'\n url pattern, instead of the \n'snippet-detail'\n url pattern.\n\n\nBecause we've included format suffixed URLs such as \n'.json'\n, we also need to indicate on the \nhighlight\n field that any format suffixed hyperlinks it returns should use the \n'.html'\n suffix.\n\n\nMaking sure our URL patterns are named\n\n\nIf we're going to have a hyperlinked API, we need to make sure we name our URL patterns. Let's take a look at which URL patterns we need to name.\n\n\n\n\nThe root of our API refers to \n'user-list'\n and \n'snippet-list'\n.\n\n\nOur snippet serializer includes a field that refers to \n'snippet-highlight'\n.\n\n\nOur user serializer includes a field that refers to \n'snippet-detail'\n.\n\n\nOur snippet and user serializers include \n'url'\n fields that by default will refer to \n'{model_name}-detail'\n, which in this case will be \n'snippet-detail'\n and \n'user-detail'\n.\n\n\n\n\nAfter adding all those names into our URLconf, our final \nsnippets/urls.py\n file should look like this:\n\n\nfrom django.urls import path\nfrom rest_framework.urlpatterns import format_suffix_patterns\nfrom snippets import views\n\n# API endpoints\nurlpatterns = format_suffix_patterns([\n path('', views.api_root),\n path('snippets/',\n views.SnippetList.as_view(),\n name='snippet-list'),\n path('snippets/\nint:pk\n/',\n views.SnippetDetail.as_view(),\n name='snippet-detail'),\n path('snippets/\nint:pk\n/highlight/',\n views.SnippetHighlight.as_view(),\n name='snippet-highlight'),\n path('users/',\n views.UserList.as_view(),\n name='user-list'),\n path('users/\nint:pk\n/',\n views.UserDetail.as_view(),\n name='user-detail')\n])\n\n\n\nAdding pagination\n\n\nThe list views for users and code snippets could end up returning quite a lot of instances, so really we'd like to make sure we paginate the results, and allow the API client to step through each of the individual pages.\n\n\nWe can change the default list style to use pagination, by modifying our \ntutorial/settings.py\n file slightly. Add the following setting:\n\n\nREST_FRAMEWORK = {\n 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',\n 'PAGE_SIZE': 10\n}\n\n\n\nNote that settings in REST framework are all namespaced into a single dictionary setting, named \nREST_FRAMEWORK\n, which helps keep them well separated from your other project settings.\n\n\nWe could also customize the pagination style if we needed too, but in this case we'll just stick with the default.\n\n\nBrowsing the API\n\n\nIf we open a browser and navigate to the browsable API, you'll find that you can now work your way around the API simply by following links.\n\n\nYou'll also be able to see the 'highlight' links on the snippet instances, that will take you to the highlighted code HTML representations.\n\n\nIn \npart 6\n of the tutorial we'll look at how we can use ViewSets and Routers to reduce the amount of code we need to build our API.",
"title": "5 - Relationships and hyperlinked APIs"
- },
+ },
{
- "location": "/tutorial/5-relationships-and-hyperlinked-apis/#tutorial-5-relationships-hyperlinked-apis",
- "text": "At the moment relationships within our API are represented by using primary keys. In this part of the tutorial we'll improve the cohesion and discoverability of our API, by instead using hyperlinking for relationships.",
+ "location": "/tutorial/5-relationships-and-hyperlinked-apis/#tutorial-5-relationships-hyperlinked-apis",
+ "text": "At the moment relationships within our API are represented by using primary keys. In this part of the tutorial we'll improve the cohesion and discoverability of our API, by instead using hyperlinking for relationships.",
"title": "Tutorial 5: Relationships & Hyperlinked APIs"
- },
+ },
{
- "location": "/tutorial/5-relationships-and-hyperlinked-apis/#creating-an-endpoint-for-the-root-of-our-api",
- "text": "Right now we have endpoints for 'snippets' and 'users', but we don't have a single entry point to our API. To create one, we'll use a regular function-based view and the @api_view decorator we introduced earlier. In your snippets/views.py add: from rest_framework.decorators import api_view\nfrom rest_framework.response import Response\nfrom rest_framework.reverse import reverse\n\n\n@api_view(['GET'])\ndef api_root(request, format=None):\n return Response({\n 'users': reverse('user-list', request=request, format=format),\n 'snippets': reverse('snippet-list', request=request, format=format)\n }) Two things should be noticed here. First, we're using REST framework's reverse function in order to return fully-qualified URLs; second, URL patterns are identified by convenience names that we will declare later on in our snippets/urls.py .",
+ "location": "/tutorial/5-relationships-and-hyperlinked-apis/#creating-an-endpoint-for-the-root-of-our-api",
+ "text": "Right now we have endpoints for 'snippets' and 'users', but we don't have a single entry point to our API. To create one, we'll use a regular function-based view and the @api_view decorator we introduced earlier. In your snippets/views.py add: from rest_framework.decorators import api_view\nfrom rest_framework.response import Response\nfrom rest_framework.reverse import reverse\n\n\n@api_view(['GET'])\ndef api_root(request, format=None):\n return Response({\n 'users': reverse('user-list', request=request, format=format),\n 'snippets': reverse('snippet-list', request=request, format=format)\n }) Two things should be noticed here. First, we're using REST framework's reverse function in order to return fully-qualified URLs; second, URL patterns are identified by convenience names that we will declare later on in our snippets/urls.py .",
"title": "Creating an endpoint for the root of our API"
- },
+ },
{
- "location": "/tutorial/5-relationships-and-hyperlinked-apis/#creating-an-endpoint-for-the-highlighted-snippets",
- "text": "The other obvious thing that's still missing from our pastebin API is the code highlighting endpoints. Unlike all our other API endpoints, we don't want to use JSON, but instead just present an HTML representation. There are two styles of HTML renderer provided by REST framework, one for dealing with HTML rendered using templates, the other for dealing with pre-rendered HTML. The second renderer is the one we'd like to use for this endpoint. The other thing we need to consider when creating the code highlight view is that there's no existing concrete generic view that we can use. We're not returning an object instance, but instead a property of an object instance. Instead of using a concrete generic view, we'll use the base class for representing instances, and create our own .get() method. In your snippets/views.py add: from rest_framework import renderers\nfrom rest_framework.response import Response\n\nclass SnippetHighlight(generics.GenericAPIView):\n queryset = Snippet.objects.all()\n renderer_classes = (renderers.StaticHTMLRenderer,)\n\n def get(self, request, *args, **kwargs):\n snippet = self.get_object()\n return Response(snippet.highlighted) As usual we need to add the new views that we've created in to our URLconf.\nWe'll add a url pattern for our new API root in snippets/urls.py : path('', views.api_root), And then add a url pattern for the snippet highlights: path('snippets//highlight/', views.SnippetHighlight.as_view()),",
+ "location": "/tutorial/5-relationships-and-hyperlinked-apis/#creating-an-endpoint-for-the-highlighted-snippets",
+ "text": "The other obvious thing that's still missing from our pastebin API is the code highlighting endpoints. Unlike all our other API endpoints, we don't want to use JSON, but instead just present an HTML representation. There are two styles of HTML renderer provided by REST framework, one for dealing with HTML rendered using templates, the other for dealing with pre-rendered HTML. The second renderer is the one we'd like to use for this endpoint. The other thing we need to consider when creating the code highlight view is that there's no existing concrete generic view that we can use. We're not returning an object instance, but instead a property of an object instance. Instead of using a concrete generic view, we'll use the base class for representing instances, and create our own .get() method. In your snippets/views.py add: from rest_framework import renderers\nfrom rest_framework.response import Response\n\nclass SnippetHighlight(generics.GenericAPIView):\n queryset = Snippet.objects.all()\n renderer_classes = (renderers.StaticHTMLRenderer,)\n\n def get(self, request, *args, **kwargs):\n snippet = self.get_object()\n return Response(snippet.highlighted) As usual we need to add the new views that we've created in to our URLconf.\nWe'll add a url pattern for our new API root in snippets/urls.py : path('', views.api_root), And then add a url pattern for the snippet highlights: path('snippets/ int:pk /highlight/', views.SnippetHighlight.as_view()),",
"title": "Creating an endpoint for the highlighted snippets"
- },
+ },
{
- "location": "/tutorial/5-relationships-and-hyperlinked-apis/#hyperlinking-our-api",
- "text": "Dealing with relationships between entities is one of the more challenging aspects of Web API design. There are a number of different ways that we might choose to represent a relationship: Using primary keys. Using hyperlinking between entities. Using a unique identifying slug field on the related entity. Using the default string representation of the related entity. Nesting the related entity inside the parent representation. Some other custom representation. REST framework supports all of these styles, and can apply them across forward or reverse relationships, or apply them across custom managers such as generic foreign keys. In this case we'd like to use a hyperlinked style between entities. In order to do so, we'll modify our serializers to extend HyperlinkedModelSerializer instead of the existing ModelSerializer . The HyperlinkedModelSerializer has the following differences from ModelSerializer : It does not include the id field by default. It includes a url field, using HyperlinkedIdentityField . Relationships use HyperlinkedRelatedField ,\n instead of PrimaryKeyRelatedField . We can easily re-write our existing serializers to use hyperlinking. In your snippets/serializers.py add: class SnippetSerializer(serializers.HyperlinkedModelSerializer):\n owner = serializers.ReadOnlyField(source='owner.username')\n highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight', format='html')\n\n class Meta:\n model = Snippet\n fields = ('url', 'id', 'highlight', 'owner',\n 'title', 'code', 'linenos', 'language', 'style')\n\n\nclass UserSerializer(serializers.HyperlinkedModelSerializer):\n snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True)\n\n class Meta:\n model = User\n fields = ('url', 'id', 'username', 'snippets') Notice that we've also added a new 'highlight' field. This field is of the same type as the url field, except that it points to the 'snippet-highlight' url pattern, instead of the 'snippet-detail' url pattern. Because we've included format suffixed URLs such as '.json' , we also need to indicate on the highlight field that any format suffixed hyperlinks it returns should use the '.html' suffix.",
+ "location": "/tutorial/5-relationships-and-hyperlinked-apis/#hyperlinking-our-api",
+ "text": "Dealing with relationships between entities is one of the more challenging aspects of Web API design. There are a number of different ways that we might choose to represent a relationship: Using primary keys. Using hyperlinking between entities. Using a unique identifying slug field on the related entity. Using the default string representation of the related entity. Nesting the related entity inside the parent representation. Some other custom representation. REST framework supports all of these styles, and can apply them across forward or reverse relationships, or apply them across custom managers such as generic foreign keys. In this case we'd like to use a hyperlinked style between entities. In order to do so, we'll modify our serializers to extend HyperlinkedModelSerializer instead of the existing ModelSerializer . The HyperlinkedModelSerializer has the following differences from ModelSerializer : It does not include the id field by default. It includes a url field, using HyperlinkedIdentityField . Relationships use HyperlinkedRelatedField ,\n instead of PrimaryKeyRelatedField . We can easily re-write our existing serializers to use hyperlinking. In your snippets/serializers.py add: class SnippetSerializer(serializers.HyperlinkedModelSerializer):\n owner = serializers.ReadOnlyField(source='owner.username')\n highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight', format='html')\n\n class Meta:\n model = Snippet\n fields = ('url', 'id', 'highlight', 'owner',\n 'title', 'code', 'linenos', 'language', 'style')\n\n\nclass UserSerializer(serializers.HyperlinkedModelSerializer):\n snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True)\n\n class Meta:\n model = User\n fields = ('url', 'id', 'username', 'snippets') Notice that we've also added a new 'highlight' field. This field is of the same type as the url field, except that it points to the 'snippet-highlight' url pattern, instead of the 'snippet-detail' url pattern. Because we've included format suffixed URLs such as '.json' , we also need to indicate on the highlight field that any format suffixed hyperlinks it returns should use the '.html' suffix.",
"title": "Hyperlinking our API"
- },
+ },
{
- "location": "/tutorial/5-relationships-and-hyperlinked-apis/#making-sure-our-url-patterns-are-named",
- "text": "If we're going to have a hyperlinked API, we need to make sure we name our URL patterns. Let's take a look at which URL patterns we need to name. The root of our API refers to 'user-list' and 'snippet-list' . Our snippet serializer includes a field that refers to 'snippet-highlight' . Our user serializer includes a field that refers to 'snippet-detail' . Our snippet and user serializers include 'url' fields that by default will refer to '{model_name}-detail' , which in this case will be 'snippet-detail' and 'user-detail' . After adding all those names into our URLconf, our final snippets/urls.py file should look like this: from django.urls import path\nfrom rest_framework.urlpatterns import format_suffix_patterns\nfrom snippets import views\n\n# API endpoints\nurlpatterns = format_suffix_patterns([\n path('', views.api_root),\n path('snippets/',\n views.SnippetList.as_view(),\n name='snippet-list'),\n path('snippets//',\n views.SnippetDetail.as_view(),\n name='snippet-detail'),\n path('snippets//highlight/',\n views.SnippetHighlight.as_view(),\n name='snippet-highlight'),\n path('users/',\n views.UserList.as_view(),\n name='user-list'),\n path('users//',\n views.UserDetail.as_view(),\n name='user-detail')\n])",
+ "location": "/tutorial/5-relationships-and-hyperlinked-apis/#making-sure-our-url-patterns-are-named",
+ "text": "If we're going to have a hyperlinked API, we need to make sure we name our URL patterns. Let's take a look at which URL patterns we need to name. The root of our API refers to 'user-list' and 'snippet-list' . Our snippet serializer includes a field that refers to 'snippet-highlight' . Our user serializer includes a field that refers to 'snippet-detail' . Our snippet and user serializers include 'url' fields that by default will refer to '{model_name}-detail' , which in this case will be 'snippet-detail' and 'user-detail' . After adding all those names into our URLconf, our final snippets/urls.py file should look like this: from django.urls import path\nfrom rest_framework.urlpatterns import format_suffix_patterns\nfrom snippets import views\n\n# API endpoints\nurlpatterns = format_suffix_patterns([\n path('', views.api_root),\n path('snippets/',\n views.SnippetList.as_view(),\n name='snippet-list'),\n path('snippets/ int:pk /',\n views.SnippetDetail.as_view(),\n name='snippet-detail'),\n path('snippets/ int:pk /highlight/',\n views.SnippetHighlight.as_view(),\n name='snippet-highlight'),\n path('users/',\n views.UserList.as_view(),\n name='user-list'),\n path('users/ int:pk /',\n views.UserDetail.as_view(),\n name='user-detail')\n])",
"title": "Making sure our URL patterns are named"
- },
+ },
{
- "location": "/tutorial/5-relationships-and-hyperlinked-apis/#adding-pagination",
- "text": "The list views for users and code snippets could end up returning quite a lot of instances, so really we'd like to make sure we paginate the results, and allow the API client to step through each of the individual pages. We can change the default list style to use pagination, by modifying our tutorial/settings.py file slightly. Add the following setting: REST_FRAMEWORK = {\n 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',\n 'PAGE_SIZE': 10\n} Note that settings in REST framework are all namespaced into a single dictionary setting, named REST_FRAMEWORK , which helps keep them well separated from your other project settings. We could also customize the pagination style if we needed too, but in this case we'll just stick with the default.",
+ "location": "/tutorial/5-relationships-and-hyperlinked-apis/#adding-pagination",
+ "text": "The list views for users and code snippets could end up returning quite a lot of instances, so really we'd like to make sure we paginate the results, and allow the API client to step through each of the individual pages. We can change the default list style to use pagination, by modifying our tutorial/settings.py file slightly. Add the following setting: REST_FRAMEWORK = {\n 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',\n 'PAGE_SIZE': 10\n} Note that settings in REST framework are all namespaced into a single dictionary setting, named REST_FRAMEWORK , which helps keep them well separated from your other project settings. We could also customize the pagination style if we needed too, but in this case we'll just stick with the default.",
"title": "Adding pagination"
- },
+ },
{
- "location": "/tutorial/5-relationships-and-hyperlinked-apis/#browsing-the-api",
- "text": "If we open a browser and navigate to the browsable API, you'll find that you can now work your way around the API simply by following links. You'll also be able to see the 'highlight' links on the snippet instances, that will take you to the highlighted code HTML representations. In part 6 of the tutorial we'll look at how we can use ViewSets and Routers to reduce the amount of code we need to build our API.",
+ "location": "/tutorial/5-relationships-and-hyperlinked-apis/#browsing-the-api",
+ "text": "If we open a browser and navigate to the browsable API, you'll find that you can now work your way around the API simply by following links. You'll also be able to see the 'highlight' links on the snippet instances, that will take you to the highlighted code HTML representations. In part 6 of the tutorial we'll look at how we can use ViewSets and Routers to reduce the amount of code we need to build our API.",
"title": "Browsing the API"
- },
+ },
{
- "location": "/tutorial/6-viewsets-and-routers/",
- "text": "Tutorial 6: ViewSets & Routers\n\n\nREST framework includes an abstraction for dealing with \nViewSets\n, that allows the developer to concentrate on modeling the state and interactions of the API, and leave the URL construction to be handled automatically, based on common conventions.\n\n\nViewSet\n classes are almost the same thing as \nView\n classes, except that they provide operations such as \nread\n, or \nupdate\n, and not method handlers such as \nget\n or \nput\n.\n\n\nA \nViewSet\n class is only bound to a set of method handlers at the last moment, when it is instantiated into a set of views, typically by using a \nRouter\n class which handles the complexities of defining the URL conf for you.\n\n\nRefactoring to use ViewSets\n\n\nLet's take our current set of views, and refactor them into view sets.\n\n\nFirst of all let's refactor our \nUserList\n and \nUserDetail\n views into a single \nUserViewSet\n. We can remove the two views, and replace them with a single class:\n\n\nfrom rest_framework import viewsets\n\nclass UserViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n This viewset automatically provides `list` and `detail` actions.\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n\n\nHere we've used the \nReadOnlyModelViewSet\n class to automatically provide the default 'read-only' operations. We're still setting the \nqueryset\n and \nserializer_class\n attributes exactly as we did when we were using regular views, but we no longer need to provide the same information to two separate classes.\n\n\nNext we're going to replace the \nSnippetList\n, \nSnippetDetail\n and \nSnippetHighlight\n view classes. We can remove the three views, and again replace them with a single class.\n\n\nfrom rest_framework.decorators import action\nfrom rest_framework.response import Response\n\nclass SnippetViewSet(viewsets.ModelViewSet):\n \"\"\"\n This viewset automatically provides `list`, `create`, `retrieve`,\n `update` and `destroy` actions.\n\n Additionally we also provide an extra `highlight` action.\n \"\"\"\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n permission_classes = (permissions.IsAuthenticatedOrReadOnly,\n IsOwnerOrReadOnly,)\n\n @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer])\n def highlight(self, request, *args, **kwargs):\n snippet = self.get_object()\n return Response(snippet.highlighted)\n\n def perform_create(self, serializer):\n serializer.save(owner=self.request.user)\n\n\n\nThis time we've used the \nModelViewSet\n class in order to get the complete set of default read and write operations.\n\n\nNotice that we've also used the \n@action\n decorator to create a custom action, named \nhighlight\n. This decorator can be used to add any custom endpoints that don't fit into the standard \ncreate\n/\nupdate\n/\ndelete\n style.\n\n\nCustom actions which use the \n@action\n decorator will respond to \nGET\n requests by default. We can use the \nmethods\n argument if we wanted an action that responded to \nPOST\n requests.\n\n\nThe URLs for custom actions by default depend on the method name itself. If you want to change the way url should be constructed, you can include \nurl_path\n as a decorator keyword argument.\n\n\nBinding ViewSets to URLs explicitly\n\n\nThe handler methods only get bound to the actions when we define the URLConf.\nTo see what's going on under the hood let's first explicitly create a set of views from our ViewSets.\n\n\nIn the \nsnippets/urls.py\n file we bind our \nViewSet\n classes into a set of concrete views.\n\n\nfrom snippets.views import SnippetViewSet, UserViewSet, api_root\nfrom rest_framework import renderers\n\nsnippet_list = SnippetViewSet.as_view({\n 'get': 'list',\n 'post': 'create'\n})\nsnippet_detail = SnippetViewSet.as_view({\n 'get': 'retrieve',\n 'put': 'update',\n 'patch': 'partial_update',\n 'delete': 'destroy'\n})\nsnippet_highlight = SnippetViewSet.as_view({\n 'get': 'highlight'\n}, renderer_classes=[renderers.StaticHTMLRenderer])\nuser_list = UserViewSet.as_view({\n 'get': 'list'\n})\nuser_detail = UserViewSet.as_view({\n 'get': 'retrieve'\n})\n\n\n\nNotice how we're creating multiple views from each \nViewSet\n class, by binding the http methods to the required action for each view.\n\n\nNow that we've bound our resources into concrete views, we can register the views with the URL conf as usual.\n\n\nurlpatterns = format_suffix_patterns([\n path('', api_root),\n path('snippets/', snippet_list, name='snippet-list'),\n path('snippets//', snippet_detail, name='snippet-detail'),\n path('snippets//highlight/', snippet_highlight, name='snippet-highlight'),\n path('users/', user_list, name='user-list'),\n path('users//', user_detail, name='user-detail')\n])\n\n\n\nUsing Routers\n\n\nBecause we're using \nViewSet\n classes rather than \nView\n classes, we actually don't need to design the URL conf ourselves. The conventions for wiring up resources into views and urls can be handled automatically, using a \nRouter\n class. All we need to do is register the appropriate view sets with a router, and let it do the rest.\n\n\nHere's our re-wired \nsnippets/urls.py\n file.\n\n\nfrom django.urls import path, include\nfrom rest_framework.routers import DefaultRouter\nfrom snippets import views\n\n# Create a router and register our viewsets with it.\nrouter = DefaultRouter()\nrouter.register(r'snippets', views.SnippetViewSet)\nrouter.register(r'users', views.UserViewSet)\n\n# The API URLs are now determined automatically by the router.\nurlpatterns = [\n path('', include(router.urls)),\n]\n\n\n\nRegistering the viewsets with the router is similar to providing a urlpattern. We include two arguments - the URL prefix for the views, and the viewset itself.\n\n\nThe \nDefaultRouter\n class we're using also automatically creates the API root view for us, so we can now delete the \napi_root\n method from our \nviews\n module.\n\n\nTrade-offs between views vs viewsets\n\n\nUsing viewsets can be a really useful abstraction. It helps ensure that URL conventions will be consistent across your API, minimizes the amount of code you need to write, and allows you to concentrate on the interactions and representations your API provides rather than the specifics of the URL conf.\n\n\nThat doesn't mean it's always the right approach to take. There's a similar set of trade-offs to consider as when using class-based views instead of function based views. Using viewsets is less explicit than building your views individually.\n\n\nIn \npart 7\n of the tutorial we'll look at how we can add an API schema,\nand interact with our API using a client library or command line tool.",
+ "location": "/tutorial/6-viewsets-and-routers/",
+ "text": "Tutorial 6: ViewSets \n Routers\n\n\nREST framework includes an abstraction for dealing with \nViewSets\n, that allows the developer to concentrate on modeling the state and interactions of the API, and leave the URL construction to be handled automatically, based on common conventions.\n\n\nViewSet\n classes are almost the same thing as \nView\n classes, except that they provide operations such as \nread\n, or \nupdate\n, and not method handlers such as \nget\n or \nput\n.\n\n\nA \nViewSet\n class is only bound to a set of method handlers at the last moment, when it is instantiated into a set of views, typically by using a \nRouter\n class which handles the complexities of defining the URL conf for you.\n\n\nRefactoring to use ViewSets\n\n\nLet's take our current set of views, and refactor them into view sets.\n\n\nFirst of all let's refactor our \nUserList\n and \nUserDetail\n views into a single \nUserViewSet\n. We can remove the two views, and replace them with a single class:\n\n\nfrom rest_framework import viewsets\n\nclass UserViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n This viewset automatically provides `list` and `detail` actions.\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n\n\nHere we've used the \nReadOnlyModelViewSet\n class to automatically provide the default 'read-only' operations. We're still setting the \nqueryset\n and \nserializer_class\n attributes exactly as we did when we were using regular views, but we no longer need to provide the same information to two separate classes.\n\n\nNext we're going to replace the \nSnippetList\n, \nSnippetDetail\n and \nSnippetHighlight\n view classes. We can remove the three views, and again replace them with a single class.\n\n\nfrom rest_framework.decorators import action\nfrom rest_framework.response import Response\n\nclass SnippetViewSet(viewsets.ModelViewSet):\n \"\"\"\n This viewset automatically provides `list`, `create`, `retrieve`,\n `update` and `destroy` actions.\n\n Additionally we also provide an extra `highlight` action.\n \"\"\"\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n permission_classes = (permissions.IsAuthenticatedOrReadOnly,\n IsOwnerOrReadOnly,)\n\n @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer])\n def highlight(self, request, *args, **kwargs):\n snippet = self.get_object()\n return Response(snippet.highlighted)\n\n def perform_create(self, serializer):\n serializer.save(owner=self.request.user)\n\n\n\nThis time we've used the \nModelViewSet\n class in order to get the complete set of default read and write operations.\n\n\nNotice that we've also used the \n@action\n decorator to create a custom action, named \nhighlight\n. This decorator can be used to add any custom endpoints that don't fit into the standard \ncreate\n/\nupdate\n/\ndelete\n style.\n\n\nCustom actions which use the \n@action\n decorator will respond to \nGET\n requests by default. We can use the \nmethods\n argument if we wanted an action that responded to \nPOST\n requests.\n\n\nThe URLs for custom actions by default depend on the method name itself. If you want to change the way url should be constructed, you can include \nurl_path\n as a decorator keyword argument.\n\n\nBinding ViewSets to URLs explicitly\n\n\nThe handler methods only get bound to the actions when we define the URLConf.\nTo see what's going on under the hood let's first explicitly create a set of views from our ViewSets.\n\n\nIn the \nsnippets/urls.py\n file we bind our \nViewSet\n classes into a set of concrete views.\n\n\nfrom snippets.views import SnippetViewSet, UserViewSet, api_root\nfrom rest_framework import renderers\n\nsnippet_list = SnippetViewSet.as_view({\n 'get': 'list',\n 'post': 'create'\n})\nsnippet_detail = SnippetViewSet.as_view({\n 'get': 'retrieve',\n 'put': 'update',\n 'patch': 'partial_update',\n 'delete': 'destroy'\n})\nsnippet_highlight = SnippetViewSet.as_view({\n 'get': 'highlight'\n}, renderer_classes=[renderers.StaticHTMLRenderer])\nuser_list = UserViewSet.as_view({\n 'get': 'list'\n})\nuser_detail = UserViewSet.as_view({\n 'get': 'retrieve'\n})\n\n\n\nNotice how we're creating multiple views from each \nViewSet\n class, by binding the http methods to the required action for each view.\n\n\nNow that we've bound our resources into concrete views, we can register the views with the URL conf as usual.\n\n\nurlpatterns = format_suffix_patterns([\n path('', api_root),\n path('snippets/', snippet_list, name='snippet-list'),\n path('snippets/\nint:pk\n/', snippet_detail, name='snippet-detail'),\n path('snippets/\nint:pk\n/highlight/', snippet_highlight, name='snippet-highlight'),\n path('users/', user_list, name='user-list'),\n path('users/\nint:pk\n/', user_detail, name='user-detail')\n])\n\n\n\nUsing Routers\n\n\nBecause we're using \nViewSet\n classes rather than \nView\n classes, we actually don't need to design the URL conf ourselves. The conventions for wiring up resources into views and urls can be handled automatically, using a \nRouter\n class. All we need to do is register the appropriate view sets with a router, and let it do the rest.\n\n\nHere's our re-wired \nsnippets/urls.py\n file.\n\n\nfrom django.urls import path, include\nfrom rest_framework.routers import DefaultRouter\nfrom snippets import views\n\n# Create a router and register our viewsets with it.\nrouter = DefaultRouter()\nrouter.register(r'snippets', views.SnippetViewSet)\nrouter.register(r'users', views.UserViewSet)\n\n# The API URLs are now determined automatically by the router.\nurlpatterns = [\n path('', include(router.urls)),\n]\n\n\n\nRegistering the viewsets with the router is similar to providing a urlpattern. We include two arguments - the URL prefix for the views, and the viewset itself.\n\n\nThe \nDefaultRouter\n class we're using also automatically creates the API root view for us, so we can now delete the \napi_root\n method from our \nviews\n module.\n\n\nTrade-offs between views vs viewsets\n\n\nUsing viewsets can be a really useful abstraction. It helps ensure that URL conventions will be consistent across your API, minimizes the amount of code you need to write, and allows you to concentrate on the interactions and representations your API provides rather than the specifics of the URL conf.\n\n\nThat doesn't mean it's always the right approach to take. There's a similar set of trade-offs to consider as when using class-based views instead of function based views. Using viewsets is less explicit than building your views individually.\n\n\nIn \npart 7\n of the tutorial we'll look at how we can add an API schema,\nand interact with our API using a client library or command line tool.",
"title": "6 - Viewsets and routers"
- },
+ },
{
- "location": "/tutorial/6-viewsets-and-routers/#tutorial-6-viewsets-routers",
- "text": "REST framework includes an abstraction for dealing with ViewSets , that allows the developer to concentrate on modeling the state and interactions of the API, and leave the URL construction to be handled automatically, based on common conventions. ViewSet classes are almost the same thing as View classes, except that they provide operations such as read , or update , and not method handlers such as get or put . A ViewSet class is only bound to a set of method handlers at the last moment, when it is instantiated into a set of views, typically by using a Router class which handles the complexities of defining the URL conf for you.",
+ "location": "/tutorial/6-viewsets-and-routers/#tutorial-6-viewsets-routers",
+ "text": "REST framework includes an abstraction for dealing with ViewSets , that allows the developer to concentrate on modeling the state and interactions of the API, and leave the URL construction to be handled automatically, based on common conventions. ViewSet classes are almost the same thing as View classes, except that they provide operations such as read , or update , and not method handlers such as get or put . A ViewSet class is only bound to a set of method handlers at the last moment, when it is instantiated into a set of views, typically by using a Router class which handles the complexities of defining the URL conf for you.",
"title": "Tutorial 6: ViewSets & Routers"
- },
+ },
{
- "location": "/tutorial/6-viewsets-and-routers/#refactoring-to-use-viewsets",
- "text": "Let's take our current set of views, and refactor them into view sets. First of all let's refactor our UserList and UserDetail views into a single UserViewSet . We can remove the two views, and replace them with a single class: from rest_framework import viewsets\n\nclass UserViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n This viewset automatically provides `list` and `detail` actions.\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer Here we've used the ReadOnlyModelViewSet class to automatically provide the default 'read-only' operations. We're still setting the queryset and serializer_class attributes exactly as we did when we were using regular views, but we no longer need to provide the same information to two separate classes. Next we're going to replace the SnippetList , SnippetDetail and SnippetHighlight view classes. We can remove the three views, and again replace them with a single class. from rest_framework.decorators import action\nfrom rest_framework.response import Response\n\nclass SnippetViewSet(viewsets.ModelViewSet):\n \"\"\"\n This viewset automatically provides `list`, `create`, `retrieve`,\n `update` and `destroy` actions.\n\n Additionally we also provide an extra `highlight` action.\n \"\"\"\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n permission_classes = (permissions.IsAuthenticatedOrReadOnly,\n IsOwnerOrReadOnly,)\n\n @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer])\n def highlight(self, request, *args, **kwargs):\n snippet = self.get_object()\n return Response(snippet.highlighted)\n\n def perform_create(self, serializer):\n serializer.save(owner=self.request.user) This time we've used the ModelViewSet class in order to get the complete set of default read and write operations. Notice that we've also used the @action decorator to create a custom action, named highlight . This decorator can be used to add any custom endpoints that don't fit into the standard create / update / delete style. Custom actions which use the @action decorator will respond to GET requests by default. We can use the methods argument if we wanted an action that responded to POST requests. The URLs for custom actions by default depend on the method name itself. If you want to change the way url should be constructed, you can include url_path as a decorator keyword argument.",
+ "location": "/tutorial/6-viewsets-and-routers/#refactoring-to-use-viewsets",
+ "text": "Let's take our current set of views, and refactor them into view sets. First of all let's refactor our UserList and UserDetail views into a single UserViewSet . We can remove the two views, and replace them with a single class: from rest_framework import viewsets\n\nclass UserViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n This viewset automatically provides `list` and `detail` actions.\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer Here we've used the ReadOnlyModelViewSet class to automatically provide the default 'read-only' operations. We're still setting the queryset and serializer_class attributes exactly as we did when we were using regular views, but we no longer need to provide the same information to two separate classes. Next we're going to replace the SnippetList , SnippetDetail and SnippetHighlight view classes. We can remove the three views, and again replace them with a single class. from rest_framework.decorators import action\nfrom rest_framework.response import Response\n\nclass SnippetViewSet(viewsets.ModelViewSet):\n \"\"\"\n This viewset automatically provides `list`, `create`, `retrieve`,\n `update` and `destroy` actions.\n\n Additionally we also provide an extra `highlight` action.\n \"\"\"\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n permission_classes = (permissions.IsAuthenticatedOrReadOnly,\n IsOwnerOrReadOnly,)\n\n @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer])\n def highlight(self, request, *args, **kwargs):\n snippet = self.get_object()\n return Response(snippet.highlighted)\n\n def perform_create(self, serializer):\n serializer.save(owner=self.request.user) This time we've used the ModelViewSet class in order to get the complete set of default read and write operations. Notice that we've also used the @action decorator to create a custom action, named highlight . This decorator can be used to add any custom endpoints that don't fit into the standard create / update / delete style. Custom actions which use the @action decorator will respond to GET requests by default. We can use the methods argument if we wanted an action that responded to POST requests. The URLs for custom actions by default depend on the method name itself. If you want to change the way url should be constructed, you can include url_path as a decorator keyword argument.",
"title": "Refactoring to use ViewSets"
- },
+ },
{
- "location": "/tutorial/6-viewsets-and-routers/#binding-viewsets-to-urls-explicitly",
- "text": "The handler methods only get bound to the actions when we define the URLConf.\nTo see what's going on under the hood let's first explicitly create a set of views from our ViewSets. In the snippets/urls.py file we bind our ViewSet classes into a set of concrete views. from snippets.views import SnippetViewSet, UserViewSet, api_root\nfrom rest_framework import renderers\n\nsnippet_list = SnippetViewSet.as_view({\n 'get': 'list',\n 'post': 'create'\n})\nsnippet_detail = SnippetViewSet.as_view({\n 'get': 'retrieve',\n 'put': 'update',\n 'patch': 'partial_update',\n 'delete': 'destroy'\n})\nsnippet_highlight = SnippetViewSet.as_view({\n 'get': 'highlight'\n}, renderer_classes=[renderers.StaticHTMLRenderer])\nuser_list = UserViewSet.as_view({\n 'get': 'list'\n})\nuser_detail = UserViewSet.as_view({\n 'get': 'retrieve'\n}) Notice how we're creating multiple views from each ViewSet class, by binding the http methods to the required action for each view. Now that we've bound our resources into concrete views, we can register the views with the URL conf as usual. urlpatterns = format_suffix_patterns([\n path('', api_root),\n path('snippets/', snippet_list, name='snippet-list'),\n path('snippets//', snippet_detail, name='snippet-detail'),\n path('snippets//highlight/', snippet_highlight, name='snippet-highlight'),\n path('users/', user_list, name='user-list'),\n path('users//', user_detail, name='user-detail')\n])",
+ "location": "/tutorial/6-viewsets-and-routers/#binding-viewsets-to-urls-explicitly",
+ "text": "The handler methods only get bound to the actions when we define the URLConf.\nTo see what's going on under the hood let's first explicitly create a set of views from our ViewSets. In the snippets/urls.py file we bind our ViewSet classes into a set of concrete views. from snippets.views import SnippetViewSet, UserViewSet, api_root\nfrom rest_framework import renderers\n\nsnippet_list = SnippetViewSet.as_view({\n 'get': 'list',\n 'post': 'create'\n})\nsnippet_detail = SnippetViewSet.as_view({\n 'get': 'retrieve',\n 'put': 'update',\n 'patch': 'partial_update',\n 'delete': 'destroy'\n})\nsnippet_highlight = SnippetViewSet.as_view({\n 'get': 'highlight'\n}, renderer_classes=[renderers.StaticHTMLRenderer])\nuser_list = UserViewSet.as_view({\n 'get': 'list'\n})\nuser_detail = UserViewSet.as_view({\n 'get': 'retrieve'\n}) Notice how we're creating multiple views from each ViewSet class, by binding the http methods to the required action for each view. Now that we've bound our resources into concrete views, we can register the views with the URL conf as usual. urlpatterns = format_suffix_patterns([\n path('', api_root),\n path('snippets/', snippet_list, name='snippet-list'),\n path('snippets/ int:pk /', snippet_detail, name='snippet-detail'),\n path('snippets/ int:pk /highlight/', snippet_highlight, name='snippet-highlight'),\n path('users/', user_list, name='user-list'),\n path('users/ int:pk /', user_detail, name='user-detail')\n])",
"title": "Binding ViewSets to URLs explicitly"
- },
+ },
{
- "location": "/tutorial/6-viewsets-and-routers/#using-routers",
- "text": "Because we're using ViewSet classes rather than View classes, we actually don't need to design the URL conf ourselves. The conventions for wiring up resources into views and urls can be handled automatically, using a Router class. All we need to do is register the appropriate view sets with a router, and let it do the rest. Here's our re-wired snippets/urls.py file. from django.urls import path, include\nfrom rest_framework.routers import DefaultRouter\nfrom snippets import views\n\n# Create a router and register our viewsets with it.\nrouter = DefaultRouter()\nrouter.register(r'snippets', views.SnippetViewSet)\nrouter.register(r'users', views.UserViewSet)\n\n# The API URLs are now determined automatically by the router.\nurlpatterns = [\n path('', include(router.urls)),\n] Registering the viewsets with the router is similar to providing a urlpattern. We include two arguments - the URL prefix for the views, and the viewset itself. The DefaultRouter class we're using also automatically creates the API root view for us, so we can now delete the api_root method from our views module.",
+ "location": "/tutorial/6-viewsets-and-routers/#using-routers",
+ "text": "Because we're using ViewSet classes rather than View classes, we actually don't need to design the URL conf ourselves. The conventions for wiring up resources into views and urls can be handled automatically, using a Router class. All we need to do is register the appropriate view sets with a router, and let it do the rest. Here's our re-wired snippets/urls.py file. from django.urls import path, include\nfrom rest_framework.routers import DefaultRouter\nfrom snippets import views\n\n# Create a router and register our viewsets with it.\nrouter = DefaultRouter()\nrouter.register(r'snippets', views.SnippetViewSet)\nrouter.register(r'users', views.UserViewSet)\n\n# The API URLs are now determined automatically by the router.\nurlpatterns = [\n path('', include(router.urls)),\n] Registering the viewsets with the router is similar to providing a urlpattern. We include two arguments - the URL prefix for the views, and the viewset itself. The DefaultRouter class we're using also automatically creates the API root view for us, so we can now delete the api_root method from our views module.",
"title": "Using Routers"
- },
+ },
{
- "location": "/tutorial/6-viewsets-and-routers/#trade-offs-between-views-vs-viewsets",
- "text": "Using viewsets can be a really useful abstraction. It helps ensure that URL conventions will be consistent across your API, minimizes the amount of code you need to write, and allows you to concentrate on the interactions and representations your API provides rather than the specifics of the URL conf. That doesn't mean it's always the right approach to take. There's a similar set of trade-offs to consider as when using class-based views instead of function based views. Using viewsets is less explicit than building your views individually. In part 7 of the tutorial we'll look at how we can add an API schema,\nand interact with our API using a client library or command line tool.",
+ "location": "/tutorial/6-viewsets-and-routers/#trade-offs-between-views-vs-viewsets",
+ "text": "Using viewsets can be a really useful abstraction. It helps ensure that URL conventions will be consistent across your API, minimizes the amount of code you need to write, and allows you to concentrate on the interactions and representations your API provides rather than the specifics of the URL conf. That doesn't mean it's always the right approach to take. There's a similar set of trade-offs to consider as when using class-based views instead of function based views. Using viewsets is less explicit than building your views individually. In part 7 of the tutorial we'll look at how we can add an API schema,\nand interact with our API using a client library or command line tool.",
"title": "Trade-offs between views vs viewsets"
- },
+ },
{
- "location": "/tutorial/7-schemas-and-client-libraries/",
- "text": "Tutorial 7: Schemas & client libraries\n\n\nA schema is a machine-readable document that describes the available API\nendpoints, their URLS, and what operations they support.\n\n\nSchemas can be a useful tool for auto-generated documentation, and can also\nbe used to drive dynamic client libraries that can interact with the API.\n\n\nCore API\n\n\nIn order to provide schema support REST framework uses \nCore API\n.\n\n\nCore API is a document specification for describing APIs. It is used to provide\nan internal representation format of the available endpoints and possible\ninteractions that an API exposes. It can either be used server-side, or\nclient-side.\n\n\nWhen used server-side, Core API allows an API to support rendering to a wide\nrange of schema or hypermedia formats.\n\n\nWhen used client-side, Core API allows for dynamically driven client libraries\nthat can interact with any API that exposes a supported schema or hypermedia\nformat.\n\n\nAdding a schema\n\n\nREST framework supports either explicitly defined schema views, or\nautomatically generated schemas. Since we're using viewsets and routers,\nwe can simply use the automatic schema generation.\n\n\nYou'll need to install the \ncoreapi\n python package in order to include an\nAPI schema, and \npyyaml\n to render the schema into the commonly used\nYAML-based OpenAPI format.\n\n\n$ pip install coreapi pyyaml\n\n\n\nWe can now include a schema for our API, by including an autogenerated schema\nview in our URL configuration.\n\n\nfrom rest_framework.schemas import get_schema_view\n\nschema_view = get_schema_view(title='Pastebin API')\n\nurlpatterns = [\n \u00a0 \u00a0path('schema/', schema_view),\n ...\n]\n\n\n\n\nIf you visit the \n/schema/\n endpoint in a browser you should now see \ncorejson\n\nrepresentation become available as an option.\n\n\n\n\nWe can also request the schema from the command line, by specifying the desired\ncontent type in the \nAccept\n header.\n\n\n$ http http://127.0.0.1:8000/schema/ Accept:application/coreapi+json\nHTTP/1.0 200 OK\nAllow: GET, HEAD, OPTIONS\nContent-Type: application/coreapi+json\n\n{\n \"_meta\": {\n \"title\": \"Pastebin API\"\n },\n \"_type\": \"document\",\n ...\n\n\n\nThe default output style is to use the \nCore JSON\n encoding.\n\n\nOther schema formats, such as \nOpen API\n (formerly Swagger) are\nalso supported.\n\n\nUsing a command line client\n\n\nNow that our API is exposing a schema endpoint, we can use a dynamic client\nlibrary to interact with the API. To demonstrate this, let's use the\nCore API command line client.\n\n\nThe command line client is available as the \ncoreapi-cli\n package:\n\n\n$ pip install coreapi-cli\n\n\n\nNow check that it is available on the command line...\n\n\n$ coreapi\nUsage: coreapi [OPTIONS] COMMAND [ARGS]...\n\n Command line client for interacting with CoreAPI services.\n\n Visit https://www.coreapi.org/ for more information.\n\nOptions:\n --version Display the package version number.\n --help Show this message and exit.\n\nCommands:\n...\n\n\n\nFirst we'll load the API schema using the command line client.\n\n\n$ coreapi get http://127.0.0.1:8000/schema/\n\n snippets: {\n highlight(id)\n list()\n read(id)\n }\n users: {\n list()\n read(id)\n }\n\n\n\nWe haven't authenticated yet, so right now we're only able to see the read only\nendpoints, in line with how we've set up the permissions on the API.\n\n\nLet's try listing the existing snippets, using the command line client:\n\n\n$ coreapi action snippets list\n[\n {\n \"url\": \"http://127.0.0.1:8000/snippets/1/\",\n \"id\": 1,\n \"highlight\": \"http://127.0.0.1:8000/snippets/1/highlight/\",\n \"owner\": \"lucy\",\n \"title\": \"Example\",\n \"code\": \"print('hello, world!')\",\n \"linenos\": true,\n \"language\": \"python\",\n \"style\": \"friendly\"\n },\n ...\n\n\n\nSome of the API endpoints require named parameters. For example, to get back\nthe highlight HTML for a particular snippet we need to provide an id.\n\n\n$ coreapi action snippets highlight --param id=1\n\n\n\n\n Example\n ...\n\n\n\nAuthenticating our client\n\n\nIf we want to be able to create, edit and delete snippets, we'll need to\nauthenticate as a valid user. In this case we'll just use basic auth.\n\n\nMake sure to replace the \n\n and \n\n below with your\nactual username and password.\n\n\n$ coreapi credentials add 127.0.0.1 : --auth basic\nAdded credentials\n127.0.0.1 \"Basic <...>\"\n\n\n\nNow if we fetch the schema again, we should be able to see the full\nset of available interactions.\n\n\n$ coreapi reload\nPastebin API \"http://127.0.0.1:8000/schema/\">\n snippets: {\n create(code, [title], [linenos], [language], [style])\n delete(id)\n highlight(id)\n list()\n partial_update(id, [title], [code], [linenos], [language], [style])\n read(id)\n update(id, code, [title], [linenos], [language], [style])\n }\n users: {\n list()\n read(id)\n }\n\n\n\nWe're now able to interact with these endpoints. For example, to create a new\nsnippet:\n\n\n$ coreapi action snippets create --param title=\"Example\" --param code=\"print('hello, world')\"\n{\n \"url\": \"http://127.0.0.1:8000/snippets/7/\",\n \"id\": 7,\n \"highlight\": \"http://127.0.0.1:8000/snippets/7/highlight/\",\n \"owner\": \"lucy\",\n \"title\": \"Example\",\n \"code\": \"print('hello, world')\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n}\n\n\n\nAnd to delete a snippet:\n\n\n$ coreapi action snippets delete --param id=7\n\n\n\nAs well as the command line client, developers can also interact with your\nAPI using client libraries. The Python client library is the first of these\nto be available, and a Javascript client library is planned to be released\nsoon.\n\n\nFor more details on customizing schema generation and using Core API\nclient libraries you'll need to refer to the full documentation.\n\n\nReviewing our work\n\n\nWith an incredibly small amount of code, we've now got a complete pastebin Web API, which is fully web browsable, includes a schema-driven client library, and comes complete with authentication, per-object permissions, and multiple renderer formats.\n\n\nWe've walked through each step of the design process, and seen how if we need to customize anything we can gradually work our way down to simply using regular Django views.\n\n\nYou can review the final \ntutorial code\n on GitHub, or try out a live example in \nthe sandbox\n.\n\n\nOnwards and upwards\n\n\nWe've reached the end of our tutorial. If you want to get more involved in the REST framework project, here are a few places you can start:\n\n\n\n\nContribute on \nGitHub\n by reviewing and submitting issues, and making pull requests.\n\n\nJoin the \nREST framework discussion group\n, and help build the community.\n\n\nFollow \nthe author\n on Twitter and say hi.\n\n\n\n\nNow go build awesome things.",
+ "location": "/tutorial/7-schemas-and-client-libraries/",
+ "text": "Tutorial 7: Schemas \n client libraries\n\n\nA schema is a machine-readable document that describes the available API\nendpoints, their URLS, and what operations they support.\n\n\nSchemas can be a useful tool for auto-generated documentation, and can also\nbe used to drive dynamic client libraries that can interact with the API.\n\n\nCore API\n\n\nIn order to provide schema support REST framework uses \nCore API\n.\n\n\nCore API is a document specification for describing APIs. It is used to provide\nan internal representation format of the available endpoints and possible\ninteractions that an API exposes. It can either be used server-side, or\nclient-side.\n\n\nWhen used server-side, Core API allows an API to support rendering to a wide\nrange of schema or hypermedia formats.\n\n\nWhen used client-side, Core API allows for dynamically driven client libraries\nthat can interact with any API that exposes a supported schema or hypermedia\nformat.\n\n\nAdding a schema\n\n\nREST framework supports either explicitly defined schema views, or\nautomatically generated schemas. Since we're using viewsets and routers,\nwe can simply use the automatic schema generation.\n\n\nYou'll need to install the \ncoreapi\n python package in order to include an\nAPI schema, and \npyyaml\n to render the schema into the commonly used\nYAML-based OpenAPI format.\n\n\n$ pip install coreapi pyyaml\n\n\n\nWe can now include a schema for our API, by including an autogenerated schema\nview in our URL configuration.\n\n\nfrom rest_framework.schemas import get_schema_view\n\nschema_view = get_schema_view(title='Pastebin API')\n\nurlpatterns = [\n \u00a0 \u00a0path('schema/', schema_view),\n ...\n]\n\n\n\n\nIf you visit the \n/schema/\n endpoint in a browser you should now see \ncorejson\n\nrepresentation become available as an option.\n\n\n\n\nWe can also request the schema from the command line, by specifying the desired\ncontent type in the \nAccept\n header.\n\n\n$ http http://127.0.0.1:8000/schema/ Accept:application/coreapi+json\nHTTP/1.0 200 OK\nAllow: GET, HEAD, OPTIONS\nContent-Type: application/coreapi+json\n\n{\n \"_meta\": {\n \"title\": \"Pastebin API\"\n },\n \"_type\": \"document\",\n ...\n\n\n\nThe default output style is to use the \nCore JSON\n encoding.\n\n\nOther schema formats, such as \nOpen API\n (formerly Swagger) are\nalso supported.\n\n\nUsing a command line client\n\n\nNow that our API is exposing a schema endpoint, we can use a dynamic client\nlibrary to interact with the API. To demonstrate this, let's use the\nCore API command line client.\n\n\nThe command line client is available as the \ncoreapi-cli\n package:\n\n\n$ pip install coreapi-cli\n\n\n\nNow check that it is available on the command line...\n\n\n$ coreapi\nUsage: coreapi [OPTIONS] COMMAND [ARGS]...\n\n Command line client for interacting with CoreAPI services.\n\n Visit https://www.coreapi.org/ for more information.\n\nOptions:\n --version Display the package version number.\n --help Show this message and exit.\n\nCommands:\n...\n\n\n\nFirst we'll load the API schema using the command line client.\n\n\n$ coreapi get http://127.0.0.1:8000/schema/\n\nPastebin API \"http://127.0.0.1:8000/schema/\"\n\n snippets: {\n highlight(id)\n list()\n read(id)\n }\n users: {\n list()\n read(id)\n }\n\n\n\nWe haven't authenticated yet, so right now we're only able to see the read only\nendpoints, in line with how we've set up the permissions on the API.\n\n\nLet's try listing the existing snippets, using the command line client:\n\n\n$ coreapi action snippets list\n[\n {\n \"url\": \"http://127.0.0.1:8000/snippets/1/\",\n \"id\": 1,\n \"highlight\": \"http://127.0.0.1:8000/snippets/1/highlight/\",\n \"owner\": \"lucy\",\n \"title\": \"Example\",\n \"code\": \"print('hello, world!')\",\n \"linenos\": true,\n \"language\": \"python\",\n \"style\": \"friendly\"\n },\n ...\n\n\n\nSome of the API endpoints require named parameters. For example, to get back\nthe highlight HTML for a particular snippet we need to provide an id.\n\n\n$ coreapi action snippets highlight --param id=1\n\n!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\"\n\n\n\nhtml\n\n\nhead\n\n \ntitle\nExample\n/title\n\n ...\n\n\n\nAuthenticating our client\n\n\nIf we want to be able to create, edit and delete snippets, we'll need to\nauthenticate as a valid user. In this case we'll just use basic auth.\n\n\nMake sure to replace the \nusername\n and \npassword\n below with your\nactual username and password.\n\n\n$ coreapi credentials add 127.0.0.1 \nusername\n:\npassword\n --auth basic\nAdded credentials\n127.0.0.1 \"Basic \n...\n\"\n\n\n\nNow if we fetch the schema again, we should be able to see the full\nset of available interactions.\n\n\n$ coreapi reload\nPastebin API \"http://127.0.0.1:8000/schema/\"\n\n snippets: {\n create(code, [title], [linenos], [language], [style])\n delete(id)\n highlight(id)\n list()\n partial_update(id, [title], [code], [linenos], [language], [style])\n read(id)\n update(id, code, [title], [linenos], [language], [style])\n }\n users: {\n list()\n read(id)\n }\n\n\n\nWe're now able to interact with these endpoints. For example, to create a new\nsnippet:\n\n\n$ coreapi action snippets create --param title=\"Example\" --param code=\"print('hello, world')\"\n{\n \"url\": \"http://127.0.0.1:8000/snippets/7/\",\n \"id\": 7,\n \"highlight\": \"http://127.0.0.1:8000/snippets/7/highlight/\",\n \"owner\": \"lucy\",\n \"title\": \"Example\",\n \"code\": \"print('hello, world')\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n}\n\n\n\nAnd to delete a snippet:\n\n\n$ coreapi action snippets delete --param id=7\n\n\n\nAs well as the command line client, developers can also interact with your\nAPI using client libraries. The Python client library is the first of these\nto be available, and a Javascript client library is planned to be released\nsoon.\n\n\nFor more details on customizing schema generation and using Core API\nclient libraries you'll need to refer to the full documentation.\n\n\nReviewing our work\n\n\nWith an incredibly small amount of code, we've now got a complete pastebin Web API, which is fully web browsable, includes a schema-driven client library, and comes complete with authentication, per-object permissions, and multiple renderer formats.\n\n\nWe've walked through each step of the design process, and seen how if we need to customize anything we can gradually work our way down to simply using regular Django views.\n\n\nYou can review the final \ntutorial code\n on GitHub, or try out a live example in \nthe sandbox\n.\n\n\nOnwards and upwards\n\n\nWe've reached the end of our tutorial. If you want to get more involved in the REST framework project, here are a few places you can start:\n\n\n\n\nContribute on \nGitHub\n by reviewing and submitting issues, and making pull requests.\n\n\nJoin the \nREST framework discussion group\n, and help build the community.\n\n\nFollow \nthe author\n on Twitter and say hi.\n\n\n\n\nNow go build awesome things.",
"title": "7 - Schemas and client libraries"
- },
+ },
{
- "location": "/tutorial/7-schemas-and-client-libraries/#tutorial-7-schemas-client-libraries",
- "text": "A schema is a machine-readable document that describes the available API\nendpoints, their URLS, and what operations they support. Schemas can be a useful tool for auto-generated documentation, and can also\nbe used to drive dynamic client libraries that can interact with the API.",
+ "location": "/tutorial/7-schemas-and-client-libraries/#tutorial-7-schemas-client-libraries",
+ "text": "A schema is a machine-readable document that describes the available API\nendpoints, their URLS, and what operations they support. Schemas can be a useful tool for auto-generated documentation, and can also\nbe used to drive dynamic client libraries that can interact with the API.",
"title": "Tutorial 7: Schemas & client libraries"
- },
+ },
{
- "location": "/tutorial/7-schemas-and-client-libraries/#core-api",
- "text": "In order to provide schema support REST framework uses Core API . Core API is a document specification for describing APIs. It is used to provide\nan internal representation format of the available endpoints and possible\ninteractions that an API exposes. It can either be used server-side, or\nclient-side. When used server-side, Core API allows an API to support rendering to a wide\nrange of schema or hypermedia formats. When used client-side, Core API allows for dynamically driven client libraries\nthat can interact with any API that exposes a supported schema or hypermedia\nformat.",
+ "location": "/tutorial/7-schemas-and-client-libraries/#core-api",
+ "text": "In order to provide schema support REST framework uses Core API . Core API is a document specification for describing APIs. It is used to provide\nan internal representation format of the available endpoints and possible\ninteractions that an API exposes. It can either be used server-side, or\nclient-side. When used server-side, Core API allows an API to support rendering to a wide\nrange of schema or hypermedia formats. When used client-side, Core API allows for dynamically driven client libraries\nthat can interact with any API that exposes a supported schema or hypermedia\nformat.",
"title": "Core API"
- },
+ },
{
- "location": "/tutorial/7-schemas-and-client-libraries/#adding-a-schema",
- "text": "REST framework supports either explicitly defined schema views, or\nautomatically generated schemas. Since we're using viewsets and routers,\nwe can simply use the automatic schema generation. You'll need to install the coreapi python package in order to include an\nAPI schema, and pyyaml to render the schema into the commonly used\nYAML-based OpenAPI format. $ pip install coreapi pyyaml We can now include a schema for our API, by including an autogenerated schema\nview in our URL configuration. from rest_framework.schemas import get_schema_view\n\nschema_view = get_schema_view(title='Pastebin API')\n\nurlpatterns = [\n \u00a0 \u00a0path('schema/', schema_view),\n ...\n] If you visit the /schema/ endpoint in a browser you should now see corejson \nrepresentation become available as an option. We can also request the schema from the command line, by specifying the desired\ncontent type in the Accept header. $ http http://127.0.0.1:8000/schema/ Accept:application/coreapi+json\nHTTP/1.0 200 OK\nAllow: GET, HEAD, OPTIONS\nContent-Type: application/coreapi+json\n\n{\n \"_meta\": {\n \"title\": \"Pastebin API\"\n },\n \"_type\": \"document\",\n ... The default output style is to use the Core JSON encoding. Other schema formats, such as Open API (formerly Swagger) are\nalso supported.",
+ "location": "/tutorial/7-schemas-and-client-libraries/#adding-a-schema",
+ "text": "REST framework supports either explicitly defined schema views, or\nautomatically generated schemas. Since we're using viewsets and routers,\nwe can simply use the automatic schema generation. You'll need to install the coreapi python package in order to include an\nAPI schema, and pyyaml to render the schema into the commonly used\nYAML-based OpenAPI format. $ pip install coreapi pyyaml We can now include a schema for our API, by including an autogenerated schema\nview in our URL configuration. from rest_framework.schemas import get_schema_view\n\nschema_view = get_schema_view(title='Pastebin API')\n\nurlpatterns = [\n \u00a0 \u00a0path('schema/', schema_view),\n ...\n] If you visit the /schema/ endpoint in a browser you should now see corejson \nrepresentation become available as an option. We can also request the schema from the command line, by specifying the desired\ncontent type in the Accept header. $ http http://127.0.0.1:8000/schema/ Accept:application/coreapi+json\nHTTP/1.0 200 OK\nAllow: GET, HEAD, OPTIONS\nContent-Type: application/coreapi+json\n\n{\n \"_meta\": {\n \"title\": \"Pastebin API\"\n },\n \"_type\": \"document\",\n ... The default output style is to use the Core JSON encoding. Other schema formats, such as Open API (formerly Swagger) are\nalso supported.",
"title": "Adding a schema"
- },
+ },
{
- "location": "/tutorial/7-schemas-and-client-libraries/#using-a-command-line-client",
- "text": "Now that our API is exposing a schema endpoint, we can use a dynamic client\nlibrary to interact with the API. To demonstrate this, let's use the\nCore API command line client. The command line client is available as the coreapi-cli package: $ pip install coreapi-cli Now check that it is available on the command line... $ coreapi\nUsage: coreapi [OPTIONS] COMMAND [ARGS]...\n\n Command line client for interacting with CoreAPI services.\n\n Visit https://www.coreapi.org/ for more information.\n\nOptions:\n --version Display the package version number.\n --help Show this message and exit.\n\nCommands:\n... First we'll load the API schema using the command line client. $ coreapi get http://127.0.0.1:8000/schema/\n\n snippets: {\n highlight(id)\n list()\n read(id)\n }\n users: {\n list()\n read(id)\n } We haven't authenticated yet, so right now we're only able to see the read only\nendpoints, in line with how we've set up the permissions on the API. Let's try listing the existing snippets, using the command line client: $ coreapi action snippets list\n[\n {\n \"url\": \"http://127.0.0.1:8000/snippets/1/\",\n \"id\": 1,\n \"highlight\": \"http://127.0.0.1:8000/snippets/1/highlight/\",\n \"owner\": \"lucy\",\n \"title\": \"Example\",\n \"code\": \"print('hello, world!')\",\n \"linenos\": true,\n \"language\": \"python\",\n \"style\": \"friendly\"\n },\n ... Some of the API endpoints require named parameters. For example, to get back\nthe highlight HTML for a particular snippet we need to provide an id. $ coreapi action snippets highlight --param id=1\n\n\n\n\n Example\n ...",
+ "location": "/tutorial/7-schemas-and-client-libraries/#using-a-command-line-client",
+ "text": "Now that our API is exposing a schema endpoint, we can use a dynamic client\nlibrary to interact with the API. To demonstrate this, let's use the\nCore API command line client. The command line client is available as the coreapi-cli package: $ pip install coreapi-cli Now check that it is available on the command line... $ coreapi\nUsage: coreapi [OPTIONS] COMMAND [ARGS]...\n\n Command line client for interacting with CoreAPI services.\n\n Visit https://www.coreapi.org/ for more information.\n\nOptions:\n --version Display the package version number.\n --help Show this message and exit.\n\nCommands:\n... First we'll load the API schema using the command line client. $ coreapi get http://127.0.0.1:8000/schema/ Pastebin API \"http://127.0.0.1:8000/schema/\" \n snippets: {\n highlight(id)\n list()\n read(id)\n }\n users: {\n list()\n read(id)\n } We haven't authenticated yet, so right now we're only able to see the read only\nendpoints, in line with how we've set up the permissions on the API. Let's try listing the existing snippets, using the command line client: $ coreapi action snippets list\n[\n {\n \"url\": \"http://127.0.0.1:8000/snippets/1/\",\n \"id\": 1,\n \"highlight\": \"http://127.0.0.1:8000/snippets/1/highlight/\",\n \"owner\": \"lucy\",\n \"title\": \"Example\",\n \"code\": \"print('hello, world!')\",\n \"linenos\": true,\n \"language\": \"python\",\n \"style\": \"friendly\"\n },\n ... Some of the API endpoints require named parameters. For example, to get back\nthe highlight HTML for a particular snippet we need to provide an id. $ coreapi action snippets highlight --param id=1 !DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\" html head \n title Example /title \n ...",
"title": "Using a command line client"
- },
+ },
{
- "location": "/tutorial/7-schemas-and-client-libraries/#authenticating-our-client",
- "text": "If we want to be able to create, edit and delete snippets, we'll need to\nauthenticate as a valid user. In this case we'll just use basic auth. Make sure to replace the and below with your\nactual username and password. $ coreapi credentials add 127.0.0.1 : --auth basic\nAdded credentials\n127.0.0.1 \"Basic <...>\" Now if we fetch the schema again, we should be able to see the full\nset of available interactions. $ coreapi reload\nPastebin API \"http://127.0.0.1:8000/schema/\">\n snippets: {\n create(code, [title], [linenos], [language], [style])\n delete(id)\n highlight(id)\n list()\n partial_update(id, [title], [code], [linenos], [language], [style])\n read(id)\n update(id, code, [title], [linenos], [language], [style])\n }\n users: {\n list()\n read(id)\n } We're now able to interact with these endpoints. For example, to create a new\nsnippet: $ coreapi action snippets create --param title=\"Example\" --param code=\"print('hello, world')\"\n{\n \"url\": \"http://127.0.0.1:8000/snippets/7/\",\n \"id\": 7,\n \"highlight\": \"http://127.0.0.1:8000/snippets/7/highlight/\",\n \"owner\": \"lucy\",\n \"title\": \"Example\",\n \"code\": \"print('hello, world')\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n} And to delete a snippet: $ coreapi action snippets delete --param id=7 As well as the command line client, developers can also interact with your\nAPI using client libraries. The Python client library is the first of these\nto be available, and a Javascript client library is planned to be released\nsoon. For more details on customizing schema generation and using Core API\nclient libraries you'll need to refer to the full documentation.",
+ "location": "/tutorial/7-schemas-and-client-libraries/#authenticating-our-client",
+ "text": "If we want to be able to create, edit and delete snippets, we'll need to\nauthenticate as a valid user. In this case we'll just use basic auth. Make sure to replace the username and password below with your\nactual username and password. $ coreapi credentials add 127.0.0.1 username : password --auth basic\nAdded credentials\n127.0.0.1 \"Basic ... \" Now if we fetch the schema again, we should be able to see the full\nset of available interactions. $ coreapi reload\nPastebin API \"http://127.0.0.1:8000/schema/\" \n snippets: {\n create(code, [title], [linenos], [language], [style])\n delete(id)\n highlight(id)\n list()\n partial_update(id, [title], [code], [linenos], [language], [style])\n read(id)\n update(id, code, [title], [linenos], [language], [style])\n }\n users: {\n list()\n read(id)\n } We're now able to interact with these endpoints. For example, to create a new\nsnippet: $ coreapi action snippets create --param title=\"Example\" --param code=\"print('hello, world')\"\n{\n \"url\": \"http://127.0.0.1:8000/snippets/7/\",\n \"id\": 7,\n \"highlight\": \"http://127.0.0.1:8000/snippets/7/highlight/\",\n \"owner\": \"lucy\",\n \"title\": \"Example\",\n \"code\": \"print('hello, world')\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n} And to delete a snippet: $ coreapi action snippets delete --param id=7 As well as the command line client, developers can also interact with your\nAPI using client libraries. The Python client library is the first of these\nto be available, and a Javascript client library is planned to be released\nsoon. For more details on customizing schema generation and using Core API\nclient libraries you'll need to refer to the full documentation.",
"title": "Authenticating our client"
- },
+ },
{
- "location": "/tutorial/7-schemas-and-client-libraries/#reviewing-our-work",
- "text": "With an incredibly small amount of code, we've now got a complete pastebin Web API, which is fully web browsable, includes a schema-driven client library, and comes complete with authentication, per-object permissions, and multiple renderer formats. We've walked through each step of the design process, and seen how if we need to customize anything we can gradually work our way down to simply using regular Django views. You can review the final tutorial code on GitHub, or try out a live example in the sandbox .",
+ "location": "/tutorial/7-schemas-and-client-libraries/#reviewing-our-work",
+ "text": "With an incredibly small amount of code, we've now got a complete pastebin Web API, which is fully web browsable, includes a schema-driven client library, and comes complete with authentication, per-object permissions, and multiple renderer formats. We've walked through each step of the design process, and seen how if we need to customize anything we can gradually work our way down to simply using regular Django views. You can review the final tutorial code on GitHub, or try out a live example in the sandbox .",
"title": "Reviewing our work"
- },
+ },
{
- "location": "/tutorial/7-schemas-and-client-libraries/#onwards-and-upwards",
- "text": "We've reached the end of our tutorial. If you want to get more involved in the REST framework project, here are a few places you can start: Contribute on GitHub by reviewing and submitting issues, and making pull requests. Join the REST framework discussion group , and help build the community. Follow the author on Twitter and say hi. Now go build awesome things.",
+ "location": "/tutorial/7-schemas-and-client-libraries/#onwards-and-upwards",
+ "text": "We've reached the end of our tutorial. If you want to get more involved in the REST framework project, here are a few places you can start: Contribute on GitHub by reviewing and submitting issues, and making pull requests. Join the REST framework discussion group , and help build the community. Follow the author on Twitter and say hi. Now go build awesome things.",
"title": "Onwards and upwards"
- },
+ },
{
- "location": "/api-guide/requests/",
- "text": "Requests\n\n\n\n\nIf you're doing REST-based web service stuff ... you should ignore request.POST.\n\n\n\u2014 Malcom Tredinnick, \nDjango developers group\n\n\n\n\nREST framework's \nRequest\n class extends the standard \nHttpRequest\n, adding support for REST framework's flexible request parsing and request authentication.\n\n\n\n\nRequest parsing\n\n\nREST framework's Request objects provide flexible request parsing that allows you to treat requests with JSON data or other media types in the same way that you would normally deal with form data.\n\n\n.data\n\n\nrequest.data\n returns the parsed content of the request body. This is similar to the standard \nrequest.POST\n and \nrequest.FILES\n attributes except that:\n\n\n\n\nIt includes all parsed content, including \nfile and non-file\n inputs.\n\n\nIt supports parsing the content of HTTP methods other than \nPOST\n, meaning that you can access the content of \nPUT\n and \nPATCH\n requests.\n\n\nIt supports REST framework's flexible request parsing, rather than just supporting form data. For example you can handle incoming JSON data in the same way that you handle incoming form data.\n\n\n\n\nFor more details see the \nparsers documentation\n.\n\n\n.query_params\n\n\nrequest.query_params\n is a more correctly named synonym for \nrequest.GET\n.\n\n\nFor clarity inside your code, we recommend using \nrequest.query_params\n instead of the Django's standard \nrequest.GET\n. Doing so will help keep your codebase more correct and obvious - any HTTP method type may include query parameters, not just \nGET\n requests.\n\n\n.parsers\n\n\nThe \nAPIView\n class or \n@api_view\n decorator will ensure that this property is automatically set to a list of \nParser\n instances, based on the \nparser_classes\n set on the view or based on the \nDEFAULT_PARSER_CLASSES\n setting.\n\n\nYou won't typically need to access this property.\n\n\n\n\nNote:\n If a client sends malformed content, then accessing \nrequest.data\n may raise a \nParseError\n. By default REST framework's \nAPIView\n class or \n@api_view\n decorator will catch the error and return a \n400 Bad Request\n response.\n\n\nIf a client sends a request with a content-type that cannot be parsed then a \nUnsupportedMediaType\n exception will be raised, which by default will be caught and return a \n415 Unsupported Media Type\n response.\n\n\n\n\nContent negotiation\n\n\nThe request exposes some properties that allow you to determine the result of the content negotiation stage. This allows you to implement behaviour such as selecting a different serialisation schemes for different media types.\n\n\n.accepted_renderer\n\n\nThe renderer instance that was selected by the content negotiation stage.\n\n\n.accepted_media_type\n\n\nA string representing the media type that was accepted by the content negotiation stage.\n\n\n\n\nAuthentication\n\n\nREST framework provides flexible, per-request authentication, that gives you the ability to:\n\n\n\n\nUse different authentication policies for different parts of your API.\n\n\nSupport the use of multiple authentication policies.\n\n\nProvide both user and token information associated with the incoming request.\n\n\n\n\n.user\n\n\nrequest.user\n typically returns an instance of \ndjango.contrib.auth.models.User\n, although the behavior depends on the authentication policy being used.\n\n\nIf the request is unauthenticated the default value of \nrequest.user\n is an instance of \ndjango.contrib.auth.models.AnonymousUser\n.\n\n\nFor more details see the \nauthentication documentation\n.\n\n\n.auth\n\n\nrequest.auth\n returns any additional authentication context. The exact behavior of \nrequest.auth\n depends on the authentication policy being used, but it may typically be an instance of the token that the request was authenticated against.\n\n\nIf the request is unauthenticated, or if no additional context is present, the default value of \nrequest.auth\n is \nNone\n.\n\n\nFor more details see the \nauthentication documentation\n.\n\n\n.authenticators\n\n\nThe \nAPIView\n class or \n@api_view\n decorator will ensure that this property is automatically set to a list of \nAuthentication\n instances, based on the \nauthentication_classes\n set on the view or based on the \nDEFAULT_AUTHENTICATORS\n setting.\n\n\nYou won't typically need to access this property.\n\n\n\n\nNote:\n You may see a \nWrappedAttributeError\n raised when calling the \n.user\n or \n.auth\n properties. These errors originate from an authenticator as a standard \nAttributeError\n, however it's necessary that they be re-raised as a different exception type in order to prevent them from being suppressed by the outer property access. Python will not recognize that the \nAttributeError\n orginates from the authenticator and will instead assume that the request object does not have a \n.user\n or \n.auth\n property. The authenticator will need to be fixed.\n\n\n\n\nBrowser enhancements\n\n\nREST framework supports a few browser enhancements such as browser-based \nPUT\n, \nPATCH\n and \nDELETE\n forms.\n\n\n.method\n\n\nrequest.method\n returns the \nuppercased\n string representation of the request's HTTP method.\n\n\nBrowser-based \nPUT\n, \nPATCH\n and \nDELETE\n forms are transparently supported.\n\n\nFor more information see the \nbrowser enhancements documentation\n.\n\n\n.content_type\n\n\nrequest.content_type\n, returns a string object representing the media type of the HTTP request's body, or an empty string if no media type was provided.\n\n\nYou won't typically need to directly access the request's content type, as you'll normally rely on REST framework's default request parsing behavior.\n\n\nIf you do need to access the content type of the request you should use the \n.content_type\n property in preference to using \nrequest.META.get('HTTP_CONTENT_TYPE')\n, as it provides transparent support for browser-based non-form content.\n\n\nFor more information see the \nbrowser enhancements documentation\n.\n\n\n.stream\n\n\nrequest.stream\n returns a stream representing the content of the request body.\n\n\nYou won't typically need to directly access the request's content, as you'll normally rely on REST framework's default request parsing behavior.\n\n\n\n\nStandard HttpRequest attributes\n\n\nAs REST framework's \nRequest\n extends Django's \nHttpRequest\n, all the other standard attributes and methods are also available. For example the \nrequest.META\n and \nrequest.session\n dictionaries are available as normal.\n\n\nNote that due to implementation reasons the \nRequest\n class does not inherit from \nHttpRequest\n class, but instead extends the class using composition.",
+ "location": "/api-guide/requests/",
+ "text": "Requests\n\n\n\n\nIf you're doing REST-based web service stuff ... you should ignore request.POST.\n\n\n Malcom Tredinnick, \nDjango developers group\n\n\n\n\nREST framework's \nRequest\n class extends the standard \nHttpRequest\n, adding support for REST framework's flexible request parsing and request authentication.\n\n\n\n\nRequest parsing\n\n\nREST framework's Request objects provide flexible request parsing that allows you to treat requests with JSON data or other media types in the same way that you would normally deal with form data.\n\n\n.data\n\n\nrequest.data\n returns the parsed content of the request body. This is similar to the standard \nrequest.POST\n and \nrequest.FILES\n attributes except that:\n\n\n\n\nIt includes all parsed content, including \nfile and non-file\n inputs.\n\n\nIt supports parsing the content of HTTP methods other than \nPOST\n, meaning that you can access the content of \nPUT\n and \nPATCH\n requests.\n\n\nIt supports REST framework's flexible request parsing, rather than just supporting form data. For example you can handle incoming JSON data in the same way that you handle incoming form data.\n\n\n\n\nFor more details see the \nparsers documentation\n.\n\n\n.query_params\n\n\nrequest.query_params\n is a more correctly named synonym for \nrequest.GET\n.\n\n\nFor clarity inside your code, we recommend using \nrequest.query_params\n instead of the Django's standard \nrequest.GET\n. Doing so will help keep your codebase more correct and obvious - any HTTP method type may include query parameters, not just \nGET\n requests.\n\n\n.parsers\n\n\nThe \nAPIView\n class or \n@api_view\n decorator will ensure that this property is automatically set to a list of \nParser\n instances, based on the \nparser_classes\n set on the view or based on the \nDEFAULT_PARSER_CLASSES\n setting.\n\n\nYou won't typically need to access this property.\n\n\n\n\nNote:\n If a client sends malformed content, then accessing \nrequest.data\n may raise a \nParseError\n. By default REST framework's \nAPIView\n class or \n@api_view\n decorator will catch the error and return a \n400 Bad Request\n response.\n\n\nIf a client sends a request with a content-type that cannot be parsed then a \nUnsupportedMediaType\n exception will be raised, which by default will be caught and return a \n415 Unsupported Media Type\n response.\n\n\n\n\nContent negotiation\n\n\nThe request exposes some properties that allow you to determine the result of the content negotiation stage. This allows you to implement behaviour such as selecting a different serialisation schemes for different media types.\n\n\n.accepted_renderer\n\n\nThe renderer instance that was selected by the content negotiation stage.\n\n\n.accepted_media_type\n\n\nA string representing the media type that was accepted by the content negotiation stage.\n\n\n\n\nAuthentication\n\n\nREST framework provides flexible, per-request authentication, that gives you the ability to:\n\n\n\n\nUse different authentication policies for different parts of your API.\n\n\nSupport the use of multiple authentication policies.\n\n\nProvide both user and token information associated with the incoming request.\n\n\n\n\n.user\n\n\nrequest.user\n typically returns an instance of \ndjango.contrib.auth.models.User\n, although the behavior depends on the authentication policy being used.\n\n\nIf the request is unauthenticated the default value of \nrequest.user\n is an instance of \ndjango.contrib.auth.models.AnonymousUser\n.\n\n\nFor more details see the \nauthentication documentation\n.\n\n\n.auth\n\n\nrequest.auth\n returns any additional authentication context. The exact behavior of \nrequest.auth\n depends on the authentication policy being used, but it may typically be an instance of the token that the request was authenticated against.\n\n\nIf the request is unauthenticated, or if no additional context is present, the default value of \nrequest.auth\n is \nNone\n.\n\n\nFor more details see the \nauthentication documentation\n.\n\n\n.authenticators\n\n\nThe \nAPIView\n class or \n@api_view\n decorator will ensure that this property is automatically set to a list of \nAuthentication\n instances, based on the \nauthentication_classes\n set on the view or based on the \nDEFAULT_AUTHENTICATORS\n setting.\n\n\nYou won't typically need to access this property.\n\n\n\n\nNote:\n You may see a \nWrappedAttributeError\n raised when calling the \n.user\n or \n.auth\n properties. These errors originate from an authenticator as a standard \nAttributeError\n, however it's necessary that they be re-raised as a different exception type in order to prevent them from being suppressed by the outer property access. Python will not recognize that the \nAttributeError\n orginates from the authenticator and will instead assume that the request object does not have a \n.user\n or \n.auth\n property. The authenticator will need to be fixed.\n\n\n\n\nBrowser enhancements\n\n\nREST framework supports a few browser enhancements such as browser-based \nPUT\n, \nPATCH\n and \nDELETE\n forms.\n\n\n.method\n\n\nrequest.method\n returns the \nuppercased\n string representation of the request's HTTP method.\n\n\nBrowser-based \nPUT\n, \nPATCH\n and \nDELETE\n forms are transparently supported.\n\n\nFor more information see the \nbrowser enhancements documentation\n.\n\n\n.content_type\n\n\nrequest.content_type\n, returns a string object representing the media type of the HTTP request's body, or an empty string if no media type was provided.\n\n\nYou won't typically need to directly access the request's content type, as you'll normally rely on REST framework's default request parsing behavior.\n\n\nIf you do need to access the content type of the request you should use the \n.content_type\n property in preference to using \nrequest.META.get('HTTP_CONTENT_TYPE')\n, as it provides transparent support for browser-based non-form content.\n\n\nFor more information see the \nbrowser enhancements documentation\n.\n\n\n.stream\n\n\nrequest.stream\n returns a stream representing the content of the request body.\n\n\nYou won't typically need to directly access the request's content, as you'll normally rely on REST framework's default request parsing behavior.\n\n\n\n\nStandard HttpRequest attributes\n\n\nAs REST framework's \nRequest\n extends Django's \nHttpRequest\n, all the other standard attributes and methods are also available. For example the \nrequest.META\n and \nrequest.session\n dictionaries are available as normal.\n\n\nNote that due to implementation reasons the \nRequest\n class does not inherit from \nHttpRequest\n class, but instead extends the class using composition.",
"title": "Requests"
- },
+ },
{
- "location": "/api-guide/requests/#requests",
- "text": "If you're doing REST-based web service stuff ... you should ignore request.POST. \u2014 Malcom Tredinnick, Django developers group REST framework's Request class extends the standard HttpRequest , adding support for REST framework's flexible request parsing and request authentication.",
+ "location": "/api-guide/requests/#requests",
+ "text": "If you're doing REST-based web service stuff ... you should ignore request.POST. Malcom Tredinnick, Django developers group REST framework's Request class extends the standard HttpRequest , adding support for REST framework's flexible request parsing and request authentication.",
"title": "Requests"
- },
+ },
{
- "location": "/api-guide/requests/#request-parsing",
- "text": "REST framework's Request objects provide flexible request parsing that allows you to treat requests with JSON data or other media types in the same way that you would normally deal with form data.",
+ "location": "/api-guide/requests/#request-parsing",
+ "text": "REST framework's Request objects provide flexible request parsing that allows you to treat requests with JSON data or other media types in the same way that you would normally deal with form data.",
"title": "Request parsing"
- },
+ },
{
- "location": "/api-guide/requests/#data",
- "text": "request.data returns the parsed content of the request body. This is similar to the standard request.POST and request.FILES attributes except that: It includes all parsed content, including file and non-file inputs. It supports parsing the content of HTTP methods other than POST , meaning that you can access the content of PUT and PATCH requests. It supports REST framework's flexible request parsing, rather than just supporting form data. For example you can handle incoming JSON data in the same way that you handle incoming form data. For more details see the parsers documentation .",
+ "location": "/api-guide/requests/#data",
+ "text": "request.data returns the parsed content of the request body. This is similar to the standard request.POST and request.FILES attributes except that: It includes all parsed content, including file and non-file inputs. It supports parsing the content of HTTP methods other than POST , meaning that you can access the content of PUT and PATCH requests. It supports REST framework's flexible request parsing, rather than just supporting form data. For example you can handle incoming JSON data in the same way that you handle incoming form data. For more details see the parsers documentation .",
"title": ".data"
- },
+ },
{
- "location": "/api-guide/requests/#query_params",
- "text": "request.query_params is a more correctly named synonym for request.GET . For clarity inside your code, we recommend using request.query_params instead of the Django's standard request.GET . Doing so will help keep your codebase more correct and obvious - any HTTP method type may include query parameters, not just GET requests.",
+ "location": "/api-guide/requests/#query_params",
+ "text": "request.query_params is a more correctly named synonym for request.GET . For clarity inside your code, we recommend using request.query_params instead of the Django's standard request.GET . Doing so will help keep your codebase more correct and obvious - any HTTP method type may include query parameters, not just GET requests.",
"title": ".query_params"
- },
+ },
{
- "location": "/api-guide/requests/#parsers",
- "text": "The APIView class or @api_view decorator will ensure that this property is automatically set to a list of Parser instances, based on the parser_classes set on the view or based on the DEFAULT_PARSER_CLASSES setting. You won't typically need to access this property. Note: If a client sends malformed content, then accessing request.data may raise a ParseError . By default REST framework's APIView class or @api_view decorator will catch the error and return a 400 Bad Request response. If a client sends a request with a content-type that cannot be parsed then a UnsupportedMediaType exception will be raised, which by default will be caught and return a 415 Unsupported Media Type response.",
+ "location": "/api-guide/requests/#parsers",
+ "text": "The APIView class or @api_view decorator will ensure that this property is automatically set to a list of Parser instances, based on the parser_classes set on the view or based on the DEFAULT_PARSER_CLASSES setting. You won't typically need to access this property. Note: If a client sends malformed content, then accessing request.data may raise a ParseError . By default REST framework's APIView class or @api_view decorator will catch the error and return a 400 Bad Request response. If a client sends a request with a content-type that cannot be parsed then a UnsupportedMediaType exception will be raised, which by default will be caught and return a 415 Unsupported Media Type response.",
"title": ".parsers"
- },
+ },
{
- "location": "/api-guide/requests/#content-negotiation",
- "text": "The request exposes some properties that allow you to determine the result of the content negotiation stage. This allows you to implement behaviour such as selecting a different serialisation schemes for different media types.",
+ "location": "/api-guide/requests/#content-negotiation",
+ "text": "The request exposes some properties that allow you to determine the result of the content negotiation stage. This allows you to implement behaviour such as selecting a different serialisation schemes for different media types.",
"title": "Content negotiation"
- },
+ },
{
- "location": "/api-guide/requests/#accepted_renderer",
- "text": "The renderer instance that was selected by the content negotiation stage.",
+ "location": "/api-guide/requests/#accepted_renderer",
+ "text": "The renderer instance that was selected by the content negotiation stage.",
"title": ".accepted_renderer"
- },
+ },
{
- "location": "/api-guide/requests/#accepted_media_type",
- "text": "A string representing the media type that was accepted by the content negotiation stage.",
+ "location": "/api-guide/requests/#accepted_media_type",
+ "text": "A string representing the media type that was accepted by the content negotiation stage.",
"title": ".accepted_media_type"
- },
+ },
{
- "location": "/api-guide/requests/#authentication",
- "text": "REST framework provides flexible, per-request authentication, that gives you the ability to: Use different authentication policies for different parts of your API. Support the use of multiple authentication policies. Provide both user and token information associated with the incoming request.",
+ "location": "/api-guide/requests/#authentication",
+ "text": "REST framework provides flexible, per-request authentication, that gives you the ability to: Use different authentication policies for different parts of your API. Support the use of multiple authentication policies. Provide both user and token information associated with the incoming request.",
"title": "Authentication"
- },
+ },
{
- "location": "/api-guide/requests/#user",
- "text": "request.user typically returns an instance of django.contrib.auth.models.User , although the behavior depends on the authentication policy being used. If the request is unauthenticated the default value of request.user is an instance of django.contrib.auth.models.AnonymousUser . For more details see the authentication documentation .",
+ "location": "/api-guide/requests/#user",
+ "text": "request.user typically returns an instance of django.contrib.auth.models.User , although the behavior depends on the authentication policy being used. If the request is unauthenticated the default value of request.user is an instance of django.contrib.auth.models.AnonymousUser . For more details see the authentication documentation .",
"title": ".user"
- },
+ },
{
- "location": "/api-guide/requests/#auth",
- "text": "request.auth returns any additional authentication context. The exact behavior of request.auth depends on the authentication policy being used, but it may typically be an instance of the token that the request was authenticated against. If the request is unauthenticated, or if no additional context is present, the default value of request.auth is None . For more details see the authentication documentation .",
+ "location": "/api-guide/requests/#auth",
+ "text": "request.auth returns any additional authentication context. The exact behavior of request.auth depends on the authentication policy being used, but it may typically be an instance of the token that the request was authenticated against. If the request is unauthenticated, or if no additional context is present, the default value of request.auth is None . For more details see the authentication documentation .",
"title": ".auth"
- },
+ },
{
- "location": "/api-guide/requests/#authenticators",
- "text": "The APIView class or @api_view decorator will ensure that this property is automatically set to a list of Authentication instances, based on the authentication_classes set on the view or based on the DEFAULT_AUTHENTICATORS setting. You won't typically need to access this property. Note: You may see a WrappedAttributeError raised when calling the .user or .auth properties. These errors originate from an authenticator as a standard AttributeError , however it's necessary that they be re-raised as a different exception type in order to prevent them from being suppressed by the outer property access. Python will not recognize that the AttributeError orginates from the authenticator and will instead assume that the request object does not have a .user or .auth property. The authenticator will need to be fixed.",
+ "location": "/api-guide/requests/#authenticators",
+ "text": "The APIView class or @api_view decorator will ensure that this property is automatically set to a list of Authentication instances, based on the authentication_classes set on the view or based on the DEFAULT_AUTHENTICATORS setting. You won't typically need to access this property. Note: You may see a WrappedAttributeError raised when calling the .user or .auth properties. These errors originate from an authenticator as a standard AttributeError , however it's necessary that they be re-raised as a different exception type in order to prevent them from being suppressed by the outer property access. Python will not recognize that the AttributeError orginates from the authenticator and will instead assume that the request object does not have a .user or .auth property. The authenticator will need to be fixed.",
"title": ".authenticators"
- },
+ },
{
- "location": "/api-guide/requests/#browser-enhancements",
- "text": "REST framework supports a few browser enhancements such as browser-based PUT , PATCH and DELETE forms.",
+ "location": "/api-guide/requests/#browser-enhancements",
+ "text": "REST framework supports a few browser enhancements such as browser-based PUT , PATCH and DELETE forms.",
"title": "Browser enhancements"
- },
+ },
{
- "location": "/api-guide/requests/#method",
- "text": "request.method returns the uppercased string representation of the request's HTTP method. Browser-based PUT , PATCH and DELETE forms are transparently supported. For more information see the browser enhancements documentation .",
+ "location": "/api-guide/requests/#method",
+ "text": "request.method returns the uppercased string representation of the request's HTTP method. Browser-based PUT , PATCH and DELETE forms are transparently supported. For more information see the browser enhancements documentation .",
"title": ".method"
- },
+ },
{
- "location": "/api-guide/requests/#content_type",
- "text": "request.content_type , returns a string object representing the media type of the HTTP request's body, or an empty string if no media type was provided. You won't typically need to directly access the request's content type, as you'll normally rely on REST framework's default request parsing behavior. If you do need to access the content type of the request you should use the .content_type property in preference to using request.META.get('HTTP_CONTENT_TYPE') , as it provides transparent support for browser-based non-form content. For more information see the browser enhancements documentation .",
+ "location": "/api-guide/requests/#content_type",
+ "text": "request.content_type , returns a string object representing the media type of the HTTP request's body, or an empty string if no media type was provided. You won't typically need to directly access the request's content type, as you'll normally rely on REST framework's default request parsing behavior. If you do need to access the content type of the request you should use the .content_type property in preference to using request.META.get('HTTP_CONTENT_TYPE') , as it provides transparent support for browser-based non-form content. For more information see the browser enhancements documentation .",
"title": ".content_type"
- },
+ },
{
- "location": "/api-guide/requests/#stream",
- "text": "request.stream returns a stream representing the content of the request body. 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.",
+ "location": "/api-guide/requests/#stream",
+ "text": "request.stream returns a stream representing the content of the request body. 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.",
"title": ".stream"
- },
+ },
{
- "location": "/api-guide/requests/#standard-httprequest-attributes",
- "text": "As REST framework's Request extends Django's HttpRequest , all the other standard attributes and methods are also available. For example the request.META and request.session dictionaries are available as normal. Note that due to implementation reasons the Request class does not inherit from HttpRequest class, but instead extends the class using composition.",
+ "location": "/api-guide/requests/#standard-httprequest-attributes",
+ "text": "As REST framework's Request extends Django's HttpRequest , all the other standard attributes and methods are also available. For example the request.META and request.session dictionaries are available as normal. Note that due to implementation reasons the Request class does not inherit from HttpRequest class, but instead extends the class using composition.",
"title": "Standard HttpRequest attributes"
- },
+ },
{
- "location": "/api-guide/responses/",
- "text": "Responses\n\n\n\n\nUnlike basic HttpResponse objects, TemplateResponse objects retain the details of the context that was provided by the view to compute the response. The final output of the response is not computed until it is needed, later in the response process.\n\n\n\u2014 \nDjango documentation\n\n\n\n\nREST framework supports HTTP content negotiation by providing a \nResponse\n class which allows you to return content that can be rendered into multiple content types, depending on the client request.\n\n\nThe \nResponse\n class subclasses Django's \nSimpleTemplateResponse\n. \nResponse\n objects are initialised with data, which should consist of native Python primitives. REST framework then uses standard HTTP content negotiation to determine how it should render the final response content.\n\n\nThere's no requirement for you to use the \nResponse\n class, you can also return regular \nHttpResponse\n or \nStreamingHttpResponse\n objects from your views if required. Using the \nResponse\n class simply provides a nicer interface for returning content-negotiated Web API responses, that can be rendered to multiple formats.\n\n\nUnless you want to heavily customize REST framework for some reason, you should always use an \nAPIView\n class or \n@api_view\n function for views that return \nResponse\n objects. Doing so ensures that the view can perform content negotiation and select the appropriate renderer for the response, before it is returned from the view.\n\n\n\n\nCreating responses\n\n\nResponse()\n\n\nSignature:\n \nResponse(data, status=None, template_name=None, headers=None, content_type=None)\n\n\nUnlike regular \nHttpResponse\n objects, you do not instantiate \nResponse\n objects with rendered content. Instead you pass in unrendered data, which may consist of any Python primitives.\n\n\nThe renderers used by the \nResponse\n class cannot natively handle complex datatypes such as Django model instances, so you need to serialize the data into primitive datatypes before creating the \nResponse\n object.\n\n\nYou can use REST framework's \nSerializer\n classes to perform this data serialization, or use your own custom serialization.\n\n\nArguments:\n\n\n\n\ndata\n: The serialized data for the response.\n\n\nstatus\n: A status code for the response. Defaults to 200. See also \nstatus codes\n.\n\n\ntemplate_name\n: A template name to use if \nHTMLRenderer\n is selected.\n\n\nheaders\n: A dictionary of HTTP headers to use in the response.\n\n\ncontent_type\n: The content type of the response. Typically, this will be set automatically by the renderer as determined by content negotiation, but there may be some cases where you need to specify the content type explicitly.\n\n\n\n\n\n\nAttributes\n\n\n.data\n\n\nThe unrendered, serialized data of the response.\n\n\n.status_code\n\n\nThe numeric status code of the HTTP response.\n\n\n.content\n\n\nThe rendered content of the response. The \n.render()\n method must have been called before \n.content\n can be accessed.\n\n\n.template_name\n\n\nThe \ntemplate_name\n, if supplied. Only required if \nHTMLRenderer\n or some other custom template renderer is the accepted renderer for the response.\n\n\n.accepted_renderer\n\n\nThe renderer instance that will be used to render the response.\n\n\nSet automatically by the \nAPIView\n or \n@api_view\n immediately before the response is returned from the view.\n\n\n.accepted_media_type\n\n\nThe media type that was selected by the content negotiation stage.\n\n\nSet automatically by the \nAPIView\n or \n@api_view\n immediately before the response is returned from the view.\n\n\n.renderer_context\n\n\nA dictionary of additional context information that will be passed to the renderer's \n.render()\n method.\n\n\nSet automatically by the \nAPIView\n or \n@api_view\n immediately before the response is returned from the view.\n\n\n\n\nStandard HttpResponse attributes\n\n\nThe \nResponse\n class extends \nSimpleTemplateResponse\n, and all the usual attributes and methods are also available on the response. For example you can set headers on the response in the standard way:\n\n\nresponse = Response()\nresponse['Cache-Control'] = 'no-cache'\n\n\n\n.render()\n\n\nSignature:\n \n.render()\n\n\nAs with any other \nTemplateResponse\n, this method is called to render the serialized data of the response into the final response content. When \n.render()\n is called, the response content will be set to the result of calling the \n.render(data, accepted_media_type, renderer_context)\n method on the \naccepted_renderer\n instance.\n\n\nYou won't typically need to call \n.render()\n yourself, as it's handled by Django's standard response cycle.",
+ "location": "/api-guide/responses/",
+ "text": "Responses\n\n\n\n\nUnlike basic HttpResponse objects, TemplateResponse objects retain the details of the context that was provided by the view to compute the response. The final output of the response is not computed until it is needed, later in the response process.\n\n\n \nDjango documentation\n\n\n\n\nREST framework supports HTTP content negotiation by providing a \nResponse\n class which allows you to return content that can be rendered into multiple content types, depending on the client request.\n\n\nThe \nResponse\n class subclasses Django's \nSimpleTemplateResponse\n. \nResponse\n objects are initialised with data, which should consist of native Python primitives. REST framework then uses standard HTTP content negotiation to determine how it should render the final response content.\n\n\nThere's no requirement for you to use the \nResponse\n class, you can also return regular \nHttpResponse\n or \nStreamingHttpResponse\n objects from your views if required. Using the \nResponse\n class simply provides a nicer interface for returning content-negotiated Web API responses, that can be rendered to multiple formats.\n\n\nUnless you want to heavily customize REST framework for some reason, you should always use an \nAPIView\n class or \n@api_view\n function for views that return \nResponse\n objects. Doing so ensures that the view can perform content negotiation and select the appropriate renderer for the response, before it is returned from the view.\n\n\n\n\nCreating responses\n\n\nResponse()\n\n\nSignature:\n \nResponse(data, status=None, template_name=None, headers=None, content_type=None)\n\n\nUnlike regular \nHttpResponse\n objects, you do not instantiate \nResponse\n objects with rendered content. Instead you pass in unrendered data, which may consist of any Python primitives.\n\n\nThe renderers used by the \nResponse\n class cannot natively handle complex datatypes such as Django model instances, so you need to serialize the data into primitive datatypes before creating the \nResponse\n object.\n\n\nYou can use REST framework's \nSerializer\n classes to perform this data serialization, or use your own custom serialization.\n\n\nArguments:\n\n\n\n\ndata\n: The serialized data for the response.\n\n\nstatus\n: A status code for the response. Defaults to 200. See also \nstatus codes\n.\n\n\ntemplate_name\n: A template name to use if \nHTMLRenderer\n is selected.\n\n\nheaders\n: A dictionary of HTTP headers to use in the response.\n\n\ncontent_type\n: The content type of the response. Typically, this will be set automatically by the renderer as determined by content negotiation, but there may be some cases where you need to specify the content type explicitly.\n\n\n\n\n\n\nAttributes\n\n\n.data\n\n\nThe unrendered, serialized data of the response.\n\n\n.status_code\n\n\nThe numeric status code of the HTTP response.\n\n\n.content\n\n\nThe rendered content of the response. The \n.render()\n method must have been called before \n.content\n can be accessed.\n\n\n.template_name\n\n\nThe \ntemplate_name\n, if supplied. Only required if \nHTMLRenderer\n or some other custom template renderer is the accepted renderer for the response.\n\n\n.accepted_renderer\n\n\nThe renderer instance that will be used to render the response.\n\n\nSet automatically by the \nAPIView\n or \n@api_view\n immediately before the response is returned from the view.\n\n\n.accepted_media_type\n\n\nThe media type that was selected by the content negotiation stage.\n\n\nSet automatically by the \nAPIView\n or \n@api_view\n immediately before the response is returned from the view.\n\n\n.renderer_context\n\n\nA dictionary of additional context information that will be passed to the renderer's \n.render()\n method.\n\n\nSet automatically by the \nAPIView\n or \n@api_view\n immediately before the response is returned from the view.\n\n\n\n\nStandard HttpResponse attributes\n\n\nThe \nResponse\n class extends \nSimpleTemplateResponse\n, and all the usual attributes and methods are also available on the response. For example you can set headers on the response in the standard way:\n\n\nresponse = Response()\nresponse['Cache-Control'] = 'no-cache'\n\n\n\n.render()\n\n\nSignature:\n \n.render()\n\n\nAs with any other \nTemplateResponse\n, this method is called to render the serialized data of the response into the final response content. When \n.render()\n is called, the response content will be set to the result of calling the \n.render(data, accepted_media_type, renderer_context)\n method on the \naccepted_renderer\n instance.\n\n\nYou won't typically need to call \n.render()\n yourself, as it's handled by Django's standard response cycle.",
"title": "Responses"
- },
+ },
{
- "location": "/api-guide/responses/#responses",
- "text": "Unlike basic HttpResponse objects, TemplateResponse objects retain the details of the context that was provided by the view to compute the response. The final output of the response is not computed until it is needed, later in the response process. \u2014 Django documentation REST framework supports HTTP content negotiation by providing a Response class which allows you to return content that can be rendered into multiple content types, depending on the client request. The Response class subclasses Django's SimpleTemplateResponse . Response objects are initialised with data, which should consist of native Python primitives. REST framework then uses standard HTTP content negotiation to determine how it should render the final response content. There's no requirement for you to use the Response class, you can also return regular HttpResponse or StreamingHttpResponse objects from your views if required. Using the Response class simply provides a nicer interface for returning content-negotiated Web API responses, that can be rendered to multiple formats. Unless you want to heavily customize REST framework for some reason, you should always use an APIView class or @api_view function for views that return Response objects. Doing so ensures that the view can perform content negotiation and select the appropriate renderer for the response, before it is returned from the view.",
+ "location": "/api-guide/responses/#responses",
+ "text": "Unlike basic HttpResponse objects, TemplateResponse objects retain the details of the context that was provided by the view to compute the response. The final output of the response is not computed until it is needed, later in the response process. Django documentation REST framework supports HTTP content negotiation by providing a Response class which allows you to return content that can be rendered into multiple content types, depending on the client request. The Response class subclasses Django's SimpleTemplateResponse . Response objects are initialised with data, which should consist of native Python primitives. REST framework then uses standard HTTP content negotiation to determine how it should render the final response content. There's no requirement for you to use the Response class, you can also return regular HttpResponse or StreamingHttpResponse objects from your views if required. Using the Response class simply provides a nicer interface for returning content-negotiated Web API responses, that can be rendered to multiple formats. Unless you want to heavily customize REST framework for some reason, you should always use an APIView class or @api_view function for views that return Response objects. Doing so ensures that the view can perform content negotiation and select the appropriate renderer for the response, before it is returned from the view.",
"title": "Responses"
- },
+ },
{
- "location": "/api-guide/responses/#creating-responses",
- "text": "",
+ "location": "/api-guide/responses/#creating-responses",
+ "text": "",
"title": "Creating responses"
- },
+ },
{
- "location": "/api-guide/responses/#response",
- "text": "Signature: Response(data, status=None, template_name=None, headers=None, content_type=None) Unlike regular HttpResponse objects, you do not instantiate Response objects with rendered content. Instead you pass in unrendered data, which may consist of any Python primitives. The renderers used by the Response class cannot natively handle complex datatypes such as Django model instances, so you need to serialize the data into primitive datatypes before creating the Response object. You can use REST framework's Serializer classes to perform this data serialization, or use your own custom serialization. Arguments: data : The serialized data for the response. status : A status code for the response. Defaults to 200. See also status codes . template_name : A template name to use if HTMLRenderer is selected. headers : A dictionary of HTTP headers to use in the response. content_type : The content type of the response. Typically, this will be set automatically by the renderer as determined by content negotiation, but there may be some cases where you need to specify the content type explicitly.",
+ "location": "/api-guide/responses/#response",
+ "text": "Signature: Response(data, status=None, template_name=None, headers=None, content_type=None) Unlike regular HttpResponse objects, you do not instantiate Response objects with rendered content. Instead you pass in unrendered data, which may consist of any Python primitives. The renderers used by the Response class cannot natively handle complex datatypes such as Django model instances, so you need to serialize the data into primitive datatypes before creating the Response object. You can use REST framework's Serializer classes to perform this data serialization, or use your own custom serialization. Arguments: data : The serialized data for the response. status : A status code for the response. Defaults to 200. See also status codes . template_name : A template name to use if HTMLRenderer is selected. headers : A dictionary of HTTP headers to use in the response. content_type : The content type of the response. Typically, this will be set automatically by the renderer as determined by content negotiation, but there may be some cases where you need to specify the content type explicitly.",
"title": "Response()"
- },
+ },
{
- "location": "/api-guide/responses/#attributes",
- "text": "",
+ "location": "/api-guide/responses/#attributes",
+ "text": "",
"title": "Attributes"
- },
+ },
{
- "location": "/api-guide/responses/#data",
- "text": "The unrendered, serialized data of the response.",
+ "location": "/api-guide/responses/#data",
+ "text": "The unrendered, serialized data of the response.",
"title": ".data"
- },
+ },
{
- "location": "/api-guide/responses/#status_code",
- "text": "The numeric status code of the HTTP response.",
+ "location": "/api-guide/responses/#status_code",
+ "text": "The numeric status code of the HTTP response.",
"title": ".status_code"
- },
+ },
{
- "location": "/api-guide/responses/#content",
- "text": "The rendered content of the response. The .render() method must have been called before .content can be accessed.",
+ "location": "/api-guide/responses/#content",
+ "text": "The rendered content of the response. The .render() method must have been called before .content can be accessed.",
"title": ".content"
- },
+ },
{
- "location": "/api-guide/responses/#template_name",
- "text": "The template_name , if supplied. Only required if HTMLRenderer or some other custom template renderer is the accepted renderer for the response.",
+ "location": "/api-guide/responses/#template_name",
+ "text": "The template_name , if supplied. Only required if HTMLRenderer or some other custom template renderer is the accepted renderer for the response.",
"title": ".template_name"
- },
+ },
{
- "location": "/api-guide/responses/#accepted_renderer",
- "text": "The renderer instance that will be used to render the response. Set automatically by the APIView or @api_view immediately before the response is returned from the view.",
+ "location": "/api-guide/responses/#accepted_renderer",
+ "text": "The renderer instance that will be used to render the response. Set automatically by the APIView or @api_view immediately before the response is returned from the view.",
"title": ".accepted_renderer"
- },
+ },
{
- "location": "/api-guide/responses/#accepted_media_type",
- "text": "The media type that was selected by the content negotiation stage. Set automatically by the APIView or @api_view immediately before the response is returned from the view.",
+ "location": "/api-guide/responses/#accepted_media_type",
+ "text": "The media type that was selected by the content negotiation stage. Set automatically by the APIView or @api_view immediately before the response is returned from the view.",
"title": ".accepted_media_type"
- },
+ },
{
- "location": "/api-guide/responses/#renderer_context",
- "text": "A dictionary of additional context information that will be passed to the renderer's .render() method. Set automatically by the APIView or @api_view immediately before the response is returned from the view.",
+ "location": "/api-guide/responses/#renderer_context",
+ "text": "A dictionary of additional context information that will be passed to the renderer's .render() method. Set automatically by the APIView or @api_view immediately before the response is returned from the view.",
"title": ".renderer_context"
- },
+ },
{
- "location": "/api-guide/responses/#standard-httpresponse-attributes",
- "text": "The Response class extends SimpleTemplateResponse , and all the usual attributes and methods are also available on the response. For example you can set headers on the response in the standard way: response = Response()\nresponse['Cache-Control'] = 'no-cache'",
+ "location": "/api-guide/responses/#standard-httpresponse-attributes",
+ "text": "The Response class extends SimpleTemplateResponse , and all the usual attributes and methods are also available on the response. For example you can set headers on the response in the standard way: response = Response()\nresponse['Cache-Control'] = 'no-cache'",
"title": "Standard HttpResponse attributes"
- },
+ },
{
- "location": "/api-guide/responses/#render",
- "text": "Signature: .render() As with any other TemplateResponse , this method is called to render the serialized data of the response into the final response content. When .render() is called, the response content will be set to the result of calling the .render(data, accepted_media_type, renderer_context) method on the accepted_renderer instance. You won't typically need to call .render() yourself, as it's handled by Django's standard response cycle.",
+ "location": "/api-guide/responses/#render",
+ "text": "Signature: .render() As with any other TemplateResponse , this method is called to render the serialized data of the response into the final response content. When .render() is called, the response content will be set to the result of calling the .render(data, accepted_media_type, renderer_context) method on the accepted_renderer instance. You won't typically need to call .render() yourself, as it's handled by Django's standard response cycle.",
"title": ".render()"
- },
+ },
{
- "location": "/api-guide/views/",
- "text": "Class-based Views\n\n\n\n\nDjango's class-based views are a welcome departure from the old-style views.\n\n\n\u2014 \nReinout van Rees\n\n\n\n\nREST framework provides an \nAPIView\n class, which subclasses Django's \nView\n class.\n\n\nAPIView\n classes are different from regular \nView\n classes in the following ways:\n\n\n\n\nRequests passed to the handler methods will be REST framework's \nRequest\n instances, not Django's \nHttpRequest\n instances.\n\n\nHandler methods may return REST framework's \nResponse\n, instead of Django's \nHttpResponse\n. The view will manage content negotiation and setting the correct renderer on the response.\n\n\nAny \nAPIException\n exceptions will be caught and mediated into appropriate responses.\n\n\nIncoming requests will be authenticated and appropriate permission and/or throttle checks will be run before dispatching the request to the handler method.\n\n\n\n\nUsing the \nAPIView\n class is pretty much the same as using a regular \nView\n class, as usual, the incoming request is dispatched to an appropriate handler method such as \n.get()\n or \n.post()\n. Additionally, a number of attributes may be set on the class that control various aspects of the API policy.\n\n\nFor example:\n\n\nfrom rest_framework.views import APIView\nfrom rest_framework.response import Response\nfrom rest_framework import authentication, permissions\nfrom django.contrib.auth.models import User\n\nclass ListUsers(APIView):\n \"\"\"\n View to list all users in the system.\n\n * Requires token authentication.\n * Only admin users are able to access this view.\n \"\"\"\n authentication_classes = (authentication.TokenAuthentication,)\n permission_classes = (permissions.IsAdminUser,)\n\n def get(self, request, format=None):\n \"\"\"\n Return a list of all users.\n \"\"\"\n usernames = [user.username for user in User.objects.all()]\n return Response(usernames)\n\n\n\n\n\nNote\n: The full methods, attributes on, and relations between Django REST Framework's \nAPIView\n, \nGenericAPIView\n, various \nMixins\n, and \nViewsets\n can be initially complex. In addition to the documentation here, the \nClassy Django REST Framework\n resource provides a browsable reference, with full methods and attributes, for each of Django REST Framework's class-based views.\n\n\n\n\nAPI policy attributes\n\n\nThe following attributes control the pluggable aspects of API views.\n\n\n.renderer_classes\n\n\n.parser_classes\n\n\n.authentication_classes\n\n\n.throttle_classes\n\n\n.permission_classes\n\n\n.content_negotiation_class\n\n\nAPI policy instantiation methods\n\n\nThe following methods are used by REST framework to instantiate the various pluggable API policies. You won't typically need to override these methods.\n\n\n.get_renderers(self)\n\n\n.get_parsers(self)\n\n\n.get_authenticators(self)\n\n\n.get_throttles(self)\n\n\n.get_permissions(self)\n\n\n.get_content_negotiator(self)\n\n\n.get_exception_handler(self)\n\n\nAPI policy implementation methods\n\n\nThe following methods are called before dispatching to the handler method.\n\n\n.check_permissions(self, request)\n\n\n.check_throttles(self, request)\n\n\n.perform_content_negotiation(self, request, force=False)\n\n\nDispatch methods\n\n\nThe following methods are called directly by the view's \n.dispatch()\n method.\nThese perform any actions that need to occur before or after calling the handler methods such as \n.get()\n, \n.post()\n, \nput()\n, \npatch()\n and \n.delete()\n.\n\n\n.initial(self, request, *args, **kwargs)\n\n\nPerforms any actions that need to occur before the handler method gets called.\nThis method is used to enforce permissions and throttling, and perform content negotiation.\n\n\nYou won't typically need to override this method.\n\n\n.handle_exception(self, exc)\n\n\nAny exception thrown by the handler method will be passed to this method, which either returns a \nResponse\n instance, or re-raises the exception.\n\n\nThe default implementation handles any subclass of \nrest_framework.exceptions.APIException\n, as well as Django's \nHttp404\n and \nPermissionDenied\n exceptions, and returns an appropriate error response.\n\n\nIf you need to customize the error responses your API returns you should subclass this method.\n\n\n.initialize_request(self, request, *args, **kwargs)\n\n\nEnsures that the request object that is passed to the handler method is an instance of \nRequest\n, rather than the usual Django \nHttpRequest\n.\n\n\nYou won't typically need to override this method.\n\n\n.finalize_response(self, request, response, *args, **kwargs)\n\n\nEnsures that any \nResponse\n object returned from the handler method will be rendered into the correct content type, as determined by the content negotiation.\n\n\nYou won't typically need to override this method.\n\n\n\n\nFunction Based Views\n\n\n\n\nSaying [that class-based views] is always the superior solution is a mistake.\n\n\n\u2014 \nNick Coghlan\n\n\n\n\nREST framework also allows you to work with regular function based views. It provides a set of simple decorators that wrap your function based views to ensure they receive an instance of \nRequest\n (rather than the usual Django \nHttpRequest\n) and allows them to return a \nResponse\n (instead of a Django \nHttpResponse\n), and allow you to configure how the request is processed.\n\n\n@api_view()\n\n\nSignature:\n \n@api_view(http_method_names=['GET'])\n\n\nThe core of this functionality is the \napi_view\n 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:\n\n\nfrom rest_framework.decorators import api_view\n\n@api_view()\ndef hello_world(request):\n return Response({\"message\": \"Hello, world!\"})\n\n\n\nThis view will use the default renderers, parsers, authentication classes etc specified in the \nsettings\n.\n\n\nBy default only \nGET\n 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:\n\n\n@api_view(['GET', 'POST'])\ndef hello_world(request):\n if request.method == 'POST':\n return Response({\"message\": \"Got some data!\", \"data\": request.data})\n return Response({\"message\": \"Hello, world!\"})\n\n\n\nAPI policy decorators\n\n\nTo override the default settings, REST framework provides a set of additional decorators which can be added to your views. These must come \nafter\n (below) the \n@api_view\n decorator. For example, to create a view that uses a \nthrottle\n to ensure it can only be called once per day by a particular user, use the \n@throttle_classes\n decorator, passing a list of throttle classes:\n\n\nfrom rest_framework.decorators import api_view, throttle_classes\nfrom rest_framework.throttling import UserRateThrottle\n\nclass OncePerDayUserThrottle(UserRateThrottle):\n rate = '1/day'\n\n@api_view(['GET'])\n@throttle_classes([OncePerDayUserThrottle])\ndef view(request):\n return Response({\"message\": \"Hello for today! See you tomorrow!\"})\n\n\n\nThese decorators correspond to the attributes set on \nAPIView\n subclasses, described above.\n\n\nThe available decorators are:\n\n\n\n\n@renderer_classes(...)\n\n\n@parser_classes(...)\n\n\n@authentication_classes(...)\n\n\n@throttle_classes(...)\n\n\n@permission_classes(...)\n\n\n\n\nEach of these decorators takes a single argument which must be a list or tuple of classes.\n\n\nView schema decorator\n\n\nTo override the default schema generation for function based views you may use\nthe \n@schema\n decorator. This must come \nafter\n (below) the \n@api_view\n\ndecorator. For example:\n\n\nfrom rest_framework.decorators import api_view, schema\nfrom rest_framework.schemas import AutoSchema\n\nclass CustomAutoSchema(AutoSchema):\n def get_link(self, path, method, base_url):\n # override view introspection here...\n\n@api_view(['GET'])\n@schema(CustomAutoSchema())\ndef view(request):\n return Response({\"message\": \"Hello for today! See you tomorrow!\"})\n\n\n\nThis decorator takes a single \nAutoSchema\n instance, an \nAutoSchema\n subclass\ninstance or \nManualSchema\n instance as described in the \nSchemas documentation\n.\nYou may pass \nNone\n in order to exclude the view from schema generation.\n\n\n@api_view(['GET'])\n@schema(None)\ndef view(request):\n return Response({\"message\": \"Will not appear in schema!\"})",
+ "location": "/api-guide/views/",
+ "text": "Class-based Views\n\n\n\n\nDjango's class-based views are a welcome departure from the old-style views.\n\n\n \nReinout van Rees\n\n\n\n\nREST framework provides an \nAPIView\n class, which subclasses Django's \nView\n class.\n\n\nAPIView\n classes are different from regular \nView\n classes in the following ways:\n\n\n\n\nRequests passed to the handler methods will be REST framework's \nRequest\n instances, not Django's \nHttpRequest\n instances.\n\n\nHandler methods may return REST framework's \nResponse\n, instead of Django's \nHttpResponse\n. The view will manage content negotiation and setting the correct renderer on the response.\n\n\nAny \nAPIException\n exceptions will be caught and mediated into appropriate responses.\n\n\nIncoming requests will be authenticated and appropriate permission and/or throttle checks will be run before dispatching the request to the handler method.\n\n\n\n\nUsing the \nAPIView\n class is pretty much the same as using a regular \nView\n class, as usual, the incoming request is dispatched to an appropriate handler method such as \n.get()\n or \n.post()\n. Additionally, a number of attributes may be set on the class that control various aspects of the API policy.\n\n\nFor example:\n\n\nfrom rest_framework.views import APIView\nfrom rest_framework.response import Response\nfrom rest_framework import authentication, permissions\nfrom django.contrib.auth.models import User\n\nclass ListUsers(APIView):\n \"\"\"\n View to list all users in the system.\n\n * Requires token authentication.\n * Only admin users are able to access this view.\n \"\"\"\n authentication_classes = (authentication.TokenAuthentication,)\n permission_classes = (permissions.IsAdminUser,)\n\n def get(self, request, format=None):\n \"\"\"\n Return a list of all users.\n \"\"\"\n usernames = [user.username for user in User.objects.all()]\n return Response(usernames)\n\n\n\n\n\nNote\n: The full methods, attributes on, and relations between Django REST Framework's \nAPIView\n, \nGenericAPIView\n, various \nMixins\n, and \nViewsets\n can be initially complex. In addition to the documentation here, the \nClassy Django REST Framework\n resource provides a browsable reference, with full methods and attributes, for each of Django REST Framework's class-based views.\n\n\n\n\nAPI policy attributes\n\n\nThe following attributes control the pluggable aspects of API views.\n\n\n.renderer_classes\n\n\n.parser_classes\n\n\n.authentication_classes\n\n\n.throttle_classes\n\n\n.permission_classes\n\n\n.content_negotiation_class\n\n\nAPI policy instantiation methods\n\n\nThe following methods are used by REST framework to instantiate the various pluggable API policies. You won't typically need to override these methods.\n\n\n.get_renderers(self)\n\n\n.get_parsers(self)\n\n\n.get_authenticators(self)\n\n\n.get_throttles(self)\n\n\n.get_permissions(self)\n\n\n.get_content_negotiator(self)\n\n\n.get_exception_handler(self)\n\n\nAPI policy implementation methods\n\n\nThe following methods are called before dispatching to the handler method.\n\n\n.check_permissions(self, request)\n\n\n.check_throttles(self, request)\n\n\n.perform_content_negotiation(self, request, force=False)\n\n\nDispatch methods\n\n\nThe following methods are called directly by the view's \n.dispatch()\n method.\nThese perform any actions that need to occur before or after calling the handler methods such as \n.get()\n, \n.post()\n, \nput()\n, \npatch()\n and \n.delete()\n.\n\n\n.initial(self, request, *args, **kwargs)\n\n\nPerforms any actions that need to occur before the handler method gets called.\nThis method is used to enforce permissions and throttling, and perform content negotiation.\n\n\nYou won't typically need to override this method.\n\n\n.handle_exception(self, exc)\n\n\nAny exception thrown by the handler method will be passed to this method, which either returns a \nResponse\n instance, or re-raises the exception.\n\n\nThe default implementation handles any subclass of \nrest_framework.exceptions.APIException\n, as well as Django's \nHttp404\n and \nPermissionDenied\n exceptions, and returns an appropriate error response.\n\n\nIf you need to customize the error responses your API returns you should subclass this method.\n\n\n.initialize_request(self, request, *args, **kwargs)\n\n\nEnsures that the request object that is passed to the handler method is an instance of \nRequest\n, rather than the usual Django \nHttpRequest\n.\n\n\nYou won't typically need to override this method.\n\n\n.finalize_response(self, request, response, *args, **kwargs)\n\n\nEnsures that any \nResponse\n object returned from the handler method will be rendered into the correct content type, as determined by the content negotiation.\n\n\nYou won't typically need to override this method.\n\n\n\n\nFunction Based Views\n\n\n\n\nSaying [that class-based views] is always the superior solution is a mistake.\n\n\n \nNick Coghlan\n\n\n\n\nREST framework also allows you to work with regular function based views. It provides a set of simple decorators that wrap your function based views to ensure they receive an instance of \nRequest\n (rather than the usual Django \nHttpRequest\n) and allows them to return a \nResponse\n (instead of a Django \nHttpResponse\n), and allow you to configure how the request is processed.\n\n\n@api_view()\n\n\nSignature:\n \n@api_view(http_method_names=['GET'])\n\n\nThe core of this functionality is the \napi_view\n 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:\n\n\nfrom rest_framework.decorators import api_view\n\n@api_view()\ndef hello_world(request):\n return Response({\"message\": \"Hello, world!\"})\n\n\n\nThis view will use the default renderers, parsers, authentication classes etc specified in the \nsettings\n.\n\n\nBy default only \nGET\n 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:\n\n\n@api_view(['GET', 'POST'])\ndef hello_world(request):\n if request.method == 'POST':\n return Response({\"message\": \"Got some data!\", \"data\": request.data})\n return Response({\"message\": \"Hello, world!\"})\n\n\n\nAPI policy decorators\n\n\nTo override the default settings, REST framework provides a set of additional decorators which can be added to your views. These must come \nafter\n (below) the \n@api_view\n decorator. For example, to create a view that uses a \nthrottle\n to ensure it can only be called once per day by a particular user, use the \n@throttle_classes\n decorator, passing a list of throttle classes:\n\n\nfrom rest_framework.decorators import api_view, throttle_classes\nfrom rest_framework.throttling import UserRateThrottle\n\nclass OncePerDayUserThrottle(UserRateThrottle):\n rate = '1/day'\n\n@api_view(['GET'])\n@throttle_classes([OncePerDayUserThrottle])\ndef view(request):\n return Response({\"message\": \"Hello for today! See you tomorrow!\"})\n\n\n\nThese decorators correspond to the attributes set on \nAPIView\n subclasses, described above.\n\n\nThe available decorators are:\n\n\n\n\n@renderer_classes(...)\n\n\n@parser_classes(...)\n\n\n@authentication_classes(...)\n\n\n@throttle_classes(...)\n\n\n@permission_classes(...)\n\n\n\n\nEach of these decorators takes a single argument which must be a list or tuple of classes.\n\n\nView schema decorator\n\n\nTo override the default schema generation for function based views you may use\nthe \n@schema\n decorator. This must come \nafter\n (below) the \n@api_view\n\ndecorator. For example:\n\n\nfrom rest_framework.decorators import api_view, schema\nfrom rest_framework.schemas import AutoSchema\n\nclass CustomAutoSchema(AutoSchema):\n def get_link(self, path, method, base_url):\n # override view introspection here...\n\n@api_view(['GET'])\n@schema(CustomAutoSchema())\ndef view(request):\n return Response({\"message\": \"Hello for today! See you tomorrow!\"})\n\n\n\nThis decorator takes a single \nAutoSchema\n instance, an \nAutoSchema\n subclass\ninstance or \nManualSchema\n instance as described in the \nSchemas documentation\n.\nYou may pass \nNone\n in order to exclude the view from schema generation.\n\n\n@api_view(['GET'])\n@schema(None)\ndef view(request):\n return Response({\"message\": \"Will not appear in schema!\"})",
"title": "Views"
- },
+ },
{
- "location": "/api-guide/views/#class-based-views",
- "text": "Django's class-based views are a welcome departure from the old-style views. \u2014 Reinout van Rees REST framework provides an APIView class, which subclasses Django's View class. APIView classes are different from regular View classes in the following ways: Requests passed to the handler methods will be REST framework's Request instances, not Django's HttpRequest instances. Handler methods may return REST framework's Response , instead of Django's HttpResponse . The view will manage content negotiation and setting the correct renderer on the response. Any APIException exceptions will be caught and mediated into appropriate responses. Incoming requests will be authenticated and appropriate permission and/or throttle checks will be run before dispatching the request to the handler method. Using the APIView class is pretty much the same as using a regular View class, as usual, the incoming request is dispatched to an appropriate handler method such as .get() or .post() . Additionally, a number of attributes may be set on the class that control various aspects of the API policy. For example: from rest_framework.views import APIView\nfrom rest_framework.response import Response\nfrom rest_framework import authentication, permissions\nfrom django.contrib.auth.models import User\n\nclass ListUsers(APIView):\n \"\"\"\n View to list all users in the system.\n\n * Requires token authentication.\n * Only admin users are able to access this view.\n \"\"\"\n authentication_classes = (authentication.TokenAuthentication,)\n permission_classes = (permissions.IsAdminUser,)\n\n def get(self, request, format=None):\n \"\"\"\n Return a list of all users.\n \"\"\"\n usernames = [user.username for user in User.objects.all()]\n return Response(usernames) Note : The full methods, attributes on, and relations between Django REST Framework's APIView , GenericAPIView , various Mixins , and Viewsets can be initially complex. In addition to the documentation here, the Classy Django REST Framework resource provides a browsable reference, with full methods and attributes, for each of Django REST Framework's class-based views.",
+ "location": "/api-guide/views/#class-based-views",
+ "text": "Django's class-based views are a welcome departure from the old-style views. Reinout van Rees REST framework provides an APIView class, which subclasses Django's View class. APIView classes are different from regular View classes in the following ways: Requests passed to the handler methods will be REST framework's Request instances, not Django's HttpRequest instances. Handler methods may return REST framework's Response , instead of Django's HttpResponse . The view will manage content negotiation and setting the correct renderer on the response. Any APIException exceptions will be caught and mediated into appropriate responses. Incoming requests will be authenticated and appropriate permission and/or throttle checks will be run before dispatching the request to the handler method. Using the APIView class is pretty much the same as using a regular View class, as usual, the incoming request is dispatched to an appropriate handler method such as .get() or .post() . Additionally, a number of attributes may be set on the class that control various aspects of the API policy. For example: from rest_framework.views import APIView\nfrom rest_framework.response import Response\nfrom rest_framework import authentication, permissions\nfrom django.contrib.auth.models import User\n\nclass ListUsers(APIView):\n \"\"\"\n View to list all users in the system.\n\n * Requires token authentication.\n * Only admin users are able to access this view.\n \"\"\"\n authentication_classes = (authentication.TokenAuthentication,)\n permission_classes = (permissions.IsAdminUser,)\n\n def get(self, request, format=None):\n \"\"\"\n Return a list of all users.\n \"\"\"\n usernames = [user.username for user in User.objects.all()]\n return Response(usernames) Note : The full methods, attributes on, and relations between Django REST Framework's APIView , GenericAPIView , various Mixins , and Viewsets can be initially complex. In addition to the documentation here, the Classy Django REST Framework resource provides a browsable reference, with full methods and attributes, for each of Django REST Framework's class-based views.",
"title": "Class-based Views"
- },
+ },
{
- "location": "/api-guide/views/#api-policy-attributes",
- "text": "The following attributes control the pluggable aspects of API views.",
+ "location": "/api-guide/views/#api-policy-attributes",
+ "text": "The following attributes control the pluggable aspects of API views.",
"title": "API policy attributes"
- },
+ },
{
- "location": "/api-guide/views/#renderer_classes",
- "text": "",
+ "location": "/api-guide/views/#renderer_classes",
+ "text": "",
"title": ".renderer_classes"
- },
+ },
{
- "location": "/api-guide/views/#parser_classes",
- "text": "",
+ "location": "/api-guide/views/#parser_classes",
+ "text": "",
"title": ".parser_classes"
- },
+ },
{
- "location": "/api-guide/views/#authentication_classes",
- "text": "",
+ "location": "/api-guide/views/#authentication_classes",
+ "text": "",
"title": ".authentication_classes"
- },
+ },
{
- "location": "/api-guide/views/#throttle_classes",
- "text": "",
+ "location": "/api-guide/views/#throttle_classes",
+ "text": "",
"title": ".throttle_classes"
- },
+ },
{
- "location": "/api-guide/views/#permission_classes",
- "text": "",
+ "location": "/api-guide/views/#permission_classes",
+ "text": "",
"title": ".permission_classes"
- },
+ },
{
- "location": "/api-guide/views/#content_negotiation_class",
- "text": "",
+ "location": "/api-guide/views/#content_negotiation_class",
+ "text": "",
"title": ".content_negotiation_class"
- },
+ },
{
- "location": "/api-guide/views/#api-policy-instantiation-methods",
- "text": "The following methods are used by REST framework to instantiate the various pluggable API policies. You won't typically need to override these methods.",
+ "location": "/api-guide/views/#api-policy-instantiation-methods",
+ "text": "The following methods are used by REST framework to instantiate the various pluggable API policies. You won't typically need to override these methods.",
"title": "API policy instantiation methods"
- },
+ },
{
- "location": "/api-guide/views/#get_renderersself",
- "text": "",
+ "location": "/api-guide/views/#get_renderersself",
+ "text": "",
"title": ".get_renderers(self)"
- },
+ },
{
- "location": "/api-guide/views/#get_parsersself",
- "text": "",
+ "location": "/api-guide/views/#get_parsersself",
+ "text": "",
"title": ".get_parsers(self)"
- },
+ },
{
- "location": "/api-guide/views/#get_authenticatorsself",
- "text": "",
+ "location": "/api-guide/views/#get_authenticatorsself",
+ "text": "",
"title": ".get_authenticators(self)"
- },
+ },
{
- "location": "/api-guide/views/#get_throttlesself",
- "text": "",
+ "location": "/api-guide/views/#get_throttlesself",
+ "text": "",
"title": ".get_throttles(self)"
- },
+ },
{
- "location": "/api-guide/views/#get_permissionsself",
- "text": "",
+ "location": "/api-guide/views/#get_permissionsself",
+ "text": "",
"title": ".get_permissions(self)"
- },
+ },
{
- "location": "/api-guide/views/#get_content_negotiatorself",
- "text": "",
+ "location": "/api-guide/views/#get_content_negotiatorself",
+ "text": "",
"title": ".get_content_negotiator(self)"
- },
+ },
{
- "location": "/api-guide/views/#get_exception_handlerself",
- "text": "",
+ "location": "/api-guide/views/#get_exception_handlerself",
+ "text": "",
"title": ".get_exception_handler(self)"
- },
+ },
{
- "location": "/api-guide/views/#api-policy-implementation-methods",
- "text": "The following methods are called before dispatching to the handler method.",
+ "location": "/api-guide/views/#api-policy-implementation-methods",
+ "text": "The following methods are called before dispatching to the handler method.",
"title": "API policy implementation methods"
- },
+ },
{
- "location": "/api-guide/views/#check_permissionsself-request",
- "text": "",
+ "location": "/api-guide/views/#check_permissionsself-request",
+ "text": "",
"title": ".check_permissions(self, request)"
- },
+ },
{
- "location": "/api-guide/views/#check_throttlesself-request",
- "text": "",
+ "location": "/api-guide/views/#check_throttlesself-request",
+ "text": "",
"title": ".check_throttles(self, request)"
- },
+ },
{
- "location": "/api-guide/views/#perform_content_negotiationself-request-forcefalse",
- "text": "",
+ "location": "/api-guide/views/#perform_content_negotiationself-request-forcefalse",
+ "text": "",
"title": ".perform_content_negotiation(self, request, force=False)"
- },
+ },
{
- "location": "/api-guide/views/#dispatch-methods",
- "text": "The following methods are called directly by the view's .dispatch() method.\nThese perform any actions that need to occur before or after calling the handler methods such as .get() , .post() , put() , patch() and .delete() .",
+ "location": "/api-guide/views/#dispatch-methods",
+ "text": "The following methods are called directly by the view's .dispatch() method.\nThese perform any actions that need to occur before or after calling the handler methods such as .get() , .post() , put() , patch() and .delete() .",
"title": "Dispatch methods"
- },
+ },
{
- "location": "/api-guide/views/#initialself-request-42args-kwargs",
- "text": "Performs any actions that need to occur before the handler method gets called.\nThis method is used to enforce permissions and throttling, and perform content negotiation. You won't typically need to override this method.",
+ "location": "/api-guide/views/#initialself-request-42args-kwargs",
+ "text": "Performs any actions that need to occur before the handler method gets called.\nThis method is used to enforce permissions and throttling, and perform content negotiation. You won't typically need to override this method.",
"title": ".initial(self, request, *args, **kwargs)"
- },
+ },
{
- "location": "/api-guide/views/#handle_exceptionself-exc",
- "text": "Any exception thrown by the handler method will be passed to this method, which either returns a Response instance, or re-raises the exception. The default implementation handles any subclass of rest_framework.exceptions.APIException , as well as Django's Http404 and PermissionDenied exceptions, and returns an appropriate error response. If you need to customize the error responses your API returns you should subclass this method.",
+ "location": "/api-guide/views/#handle_exceptionself-exc",
+ "text": "Any exception thrown by the handler method will be passed to this method, which either returns a Response instance, or re-raises the exception. The default implementation handles any subclass of rest_framework.exceptions.APIException , as well as Django's Http404 and PermissionDenied exceptions, and returns an appropriate error response. If you need to customize the error responses your API returns you should subclass this method.",
"title": ".handle_exception(self, exc)"
- },
+ },
{
- "location": "/api-guide/views/#initialize_requestself-request-42args-kwargs",
- "text": "Ensures that the request object that is passed to the handler method is an instance of Request , rather than the usual Django HttpRequest . You won't typically need to override this method.",
+ "location": "/api-guide/views/#initialize_requestself-request-42args-kwargs",
+ "text": "Ensures that the request object that is passed to the handler method is an instance of Request , rather than the usual Django HttpRequest . You won't typically need to override this method.",
"title": ".initialize_request(self, request, *args, **kwargs)"
- },
+ },
{
- "location": "/api-guide/views/#finalize_responseself-request-response-42args-kwargs",
- "text": "Ensures that any Response object returned from the handler method will be rendered into the correct content type, as determined by the content negotiation. You won't typically need to override this method.",
+ "location": "/api-guide/views/#finalize_responseself-request-response-42args-kwargs",
+ "text": "Ensures that any Response object returned from the handler method will be rendered into the correct content type, as determined by the content negotiation. You won't typically need to override this method.",
"title": ".finalize_response(self, request, response, *args, **kwargs)"
- },
+ },
{
- "location": "/api-guide/views/#function-based-views",
- "text": "Saying [that class-based views] is always the superior solution is a mistake. \u2014 Nick Coghlan REST framework also allows you to work with regular function based views. It provides a set of simple decorators that wrap your function based views to ensure they receive an instance of Request (rather than the usual Django HttpRequest ) and allows them to return a Response (instead of a Django HttpResponse ), and allow you to configure how the request is processed.",
+ "location": "/api-guide/views/#function-based-views",
+ "text": "Saying [that class-based views] is always the superior solution is a mistake. Nick Coghlan REST framework also allows you to work with regular function based views. It provides a set of simple decorators that wrap your function based views to ensure they receive an instance of Request (rather than the usual Django HttpRequest ) and allows them to return a Response (instead of a Django HttpResponse ), and allow you to configure how the request is processed.",
"title": "Function Based Views"
- },
+ },
{
- "location": "/api-guide/views/#api_view",
- "text": "Signature: @api_view(http_method_names=['GET']) 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: from rest_framework.decorators import api_view\n\n@api_view()\ndef hello_world(request):\n return Response({\"message\": \"Hello, world!\"}) 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 behaviour, specify which methods the view allows, like so: @api_view(['GET', 'POST'])\ndef hello_world(request):\n if request.method == 'POST':\n return Response({\"message\": \"Got some data!\", \"data\": request.data})\n return Response({\"message\": \"Hello, world!\"})",
+ "location": "/api-guide/views/#api_view",
+ "text": "Signature: @api_view(http_method_names=['GET']) 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: from rest_framework.decorators import api_view\n\n@api_view()\ndef hello_world(request):\n return Response({\"message\": \"Hello, world!\"}) 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 behaviour, specify which methods the view allows, like so: @api_view(['GET', 'POST'])\ndef hello_world(request):\n if request.method == 'POST':\n return Response({\"message\": \"Got some data!\", \"data\": request.data})\n return Response({\"message\": \"Hello, world!\"})",
"title": "@api_view()"
- },
+ },
{
- "location": "/api-guide/views/#api-policy-decorators",
- "text": "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 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: from rest_framework.decorators import api_view, throttle_classes\nfrom rest_framework.throttling import UserRateThrottle\n\nclass OncePerDayUserThrottle(UserRateThrottle):\n rate = '1/day'\n\n@api_view(['GET'])\n@throttle_classes([OncePerDayUserThrottle])\ndef view(request):\n return Response({\"message\": \"Hello for today! See you tomorrow!\"}) These decorators correspond to the attributes set on APIView subclasses, described above. The available decorators are: @renderer_classes(...) @parser_classes(...) @authentication_classes(...) @throttle_classes(...) @permission_classes(...) Each of these decorators takes a single argument which must be a list or tuple of classes.",
+ "location": "/api-guide/views/#api-policy-decorators",
+ "text": "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 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: from rest_framework.decorators import api_view, throttle_classes\nfrom rest_framework.throttling import UserRateThrottle\n\nclass OncePerDayUserThrottle(UserRateThrottle):\n rate = '1/day'\n\n@api_view(['GET'])\n@throttle_classes([OncePerDayUserThrottle])\ndef view(request):\n return Response({\"message\": \"Hello for today! See you tomorrow!\"}) These decorators correspond to the attributes set on APIView subclasses, described above. The available decorators are: @renderer_classes(...) @parser_classes(...) @authentication_classes(...) @throttle_classes(...) @permission_classes(...) Each of these decorators takes a single argument which must be a list or tuple of classes.",
"title": "API policy decorators"
- },
+ },
{
- "location": "/api-guide/views/#view-schema-decorator",
- "text": "To override the default schema generation for function based views you may use\nthe @schema decorator. This must come after (below) the @api_view \ndecorator. For example: from rest_framework.decorators import api_view, schema\nfrom rest_framework.schemas import AutoSchema\n\nclass CustomAutoSchema(AutoSchema):\n def get_link(self, path, method, base_url):\n # override view introspection here...\n\n@api_view(['GET'])\n@schema(CustomAutoSchema())\ndef view(request):\n return Response({\"message\": \"Hello for today! See you tomorrow!\"}) This decorator takes a single AutoSchema instance, an AutoSchema subclass\ninstance or ManualSchema instance as described in the Schemas documentation .\nYou may pass None in order to exclude the view from schema generation. @api_view(['GET'])\n@schema(None)\ndef view(request):\n return Response({\"message\": \"Will not appear in schema!\"})",
+ "location": "/api-guide/views/#view-schema-decorator",
+ "text": "To override the default schema generation for function based views you may use\nthe @schema decorator. This must come after (below) the @api_view \ndecorator. For example: from rest_framework.decorators import api_view, schema\nfrom rest_framework.schemas import AutoSchema\n\nclass CustomAutoSchema(AutoSchema):\n def get_link(self, path, method, base_url):\n # override view introspection here...\n\n@api_view(['GET'])\n@schema(CustomAutoSchema())\ndef view(request):\n return Response({\"message\": \"Hello for today! See you tomorrow!\"}) This decorator takes a single AutoSchema instance, an AutoSchema subclass\ninstance or ManualSchema instance as described in the Schemas documentation .\nYou may pass None in order to exclude the view from schema generation. @api_view(['GET'])\n@schema(None)\ndef view(request):\n return Response({\"message\": \"Will not appear in schema!\"})",
"title": "View schema decorator"
- },
+ },
{
- "location": "/api-guide/generic-views/",
- "text": "Generic views\n\n\n\n\nDjango\u2019s generic views... were developed as a shortcut for common usage patterns... They take certain common idioms and patterns found in view development and abstract them so that you can quickly write common views of data without having to repeat yourself.\n\n\n\u2014 \nDjango Documentation\n\n\n\n\nOne 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.\n\n\nThe generic views provided by REST framework allow you to quickly build API views that map closely to your database models.\n\n\nIf the generic views don't suit the needs of your API, you can drop down to using the regular \nAPIView\n class, or reuse the mixins and base classes used by the generic views to compose your own set of reusable generic views.\n\n\nExamples\n\n\nTypically when using the generic views, you'll override the view, and set several class attributes.\n\n\nfrom django.contrib.auth.models import User\nfrom myapp.serializers import UserSerializer\nfrom rest_framework import generics\nfrom rest_framework.permissions import IsAdminUser\n\nclass UserList(generics.ListCreateAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n permission_classes = (IsAdminUser,)\n\n\n\nFor more complex cases you might also want to override various methods on the view class. For example.\n\n\nclass UserList(generics.ListCreateAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n permission_classes = (IsAdminUser,)\n\n def list(self, request):\n # Note the use of `get_queryset()` instead of `self.queryset`\n queryset = self.get_queryset()\n serializer = UserSerializer(queryset, many=True)\n return Response(serializer.data)\n\n\n\nFor very simple cases you might want to pass through any class attributes using the \n.as_view()\n method. For example, your URLconf might include something like the following entry:\n\n\nurl(r'^/users/', ListCreateAPIView.as_view(queryset=User.objects.all(), serializer_class=UserSerializer), name='user-list')\n\n\n\n\n\nAPI Reference\n\n\nGenericAPIView\n\n\nThis class extends REST framework's \nAPIView\n class, adding commonly required behavior for standard list and detail views.\n\n\nEach of the concrete generic views provided is built by combining \nGenericAPIView\n, with one or more mixin classes.\n\n\nAttributes\n\n\nBasic settings\n:\n\n\nThe following attributes control the basic view behavior.\n\n\n\n\nqueryset\n - The queryset that should be used for returning objects from this view. Typically, you must either set this attribute, or override the \nget_queryset()\n method. If you are overriding a view method, it is important that you call \nget_queryset()\n instead of accessing this property directly, as \nqueryset\n will get evaluated once, and those results will be cached for all subsequent requests.\n\n\nserializer_class\n - The serializer class that should be used for validating and deserializing input, and for serializing output. Typically, you must either set this attribute, or override the \nget_serializer_class()\n method.\n\n\nlookup_field\n - The model field that should be used to for performing object lookup of individual model instances. Defaults to \n'pk'\n. Note that when using hyperlinked APIs you'll need to ensure that \nboth\n the API views \nand\n the serializer classes set the lookup fields if you need to use a custom value.\n\n\nlookup_url_kwarg\n - The URL keyword argument that should be used for object lookup. The URL conf should include a keyword argument corresponding to this value. If unset this defaults to using the same value as \nlookup_field\n.\n\n\n\n\nPagination\n:\n\n\nThe following attributes are used to control pagination when used with list views.\n\n\n\n\npagination_class\n - The pagination class that should be used when paginating list results. Defaults to the same value as the \nDEFAULT_PAGINATION_CLASS\n setting, which is \n'rest_framework.pagination.PageNumberPagination'\n. Setting \npagination_class=None\n will disable pagination on this view.\n\n\n\n\nFiltering\n:\n\n\n\n\nfilter_backends\n - A list of filter backend classes that should be used for filtering the queryset. Defaults to the same value as the \nDEFAULT_FILTER_BACKENDS\n setting.\n\n\n\n\nMethods\n\n\nBase methods\n:\n\n\nget_queryset(self)\n\n\nReturns the queryset that should be used for list views, and that should be used as the base for lookups in detail views. Defaults to returning the queryset specified by the \nqueryset\n attribute.\n\n\nThis method should always be used rather than accessing \nself.queryset\n directly, as \nself.queryset\n gets evaluated only once, and those results are cached for all subsequent requests.\n\n\nMay be overridden to provide dynamic behavior, such as returning a queryset, that is specific to the user making the request.\n\n\nFor example:\n\n\ndef get_queryset(self):\n user = self.request.user\n return user.accounts.all()\n\n\n\nget_object(self)\n\n\nReturns an object instance that should be used for detail views. Defaults to using the \nlookup_field\n parameter to filter the base queryset.\n\n\nMay be overridden to provide more complex behavior, such as object lookups based on more than one URL kwarg.\n\n\nFor example:\n\n\ndef get_object(self):\n queryset = self.get_queryset()\n filter = {}\n for field in self.multiple_lookup_fields:\n filter[field] = self.kwargs[field]\n\n obj = get_object_or_404(queryset, **filter)\n self.check_object_permissions(self.request, obj)\n return obj\n\n\n\nNote that if your API doesn't include any object level permissions, you may optionally exclude the \nself.check_object_permissions\n, and simply return the object from the \nget_object_or_404\n lookup.\n\n\nfilter_queryset(self, queryset)\n\n\nGiven a queryset, filter it with whichever filter backends are in use, returning a new queryset.\n\n\nFor example:\n\n\ndef filter_queryset(self, queryset):\n filter_backends = (CategoryFilter,)\n\n if 'geo_route' in self.request.query_params:\n filter_backends = (GeoRouteFilter, CategoryFilter)\n elif 'geo_point' in self.request.query_params:\n filter_backends = (GeoPointFilter, CategoryFilter)\n\n for backend in list(filter_backends):\n queryset = backend().filter_queryset(self.request, queryset, view=self)\n\n return queryset\n\n\n\nget_serializer_class(self)\n\n\nReturns the class that should be used for the serializer. Defaults to returning the \nserializer_class\n attribute.\n\n\nMay be overridden to provide dynamic behavior, such as using different serializers for read and write operations, or providing different serializers to different types of users.\n\n\nFor example:\n\n\ndef get_serializer_class(self):\n if self.request.user.is_staff:\n return FullAccountSerializer\n return BasicAccountSerializer\n\n\n\nSave and deletion hooks\n:\n\n\nThe following methods are provided by the mixin classes, and provide easy overriding of the object save or deletion behavior.\n\n\n\n\nperform_create(self, serializer)\n - Called by \nCreateModelMixin\n when saving a new object instance.\n\n\nperform_update(self, serializer)\n - Called by \nUpdateModelMixin\n when saving an existing object instance.\n\n\nperform_destroy(self, instance)\n - Called by \nDestroyModelMixin\n when deleting an object instance.\n\n\n\n\nThese hooks are particularly useful for setting attributes that are implicit in the request, but are not part of the request data. For instance, you might set an attribute on the object based on the request user, or based on a URL keyword argument.\n\n\ndef perform_create(self, serializer):\n serializer.save(user=self.request.user)\n\n\n\nThese override points are also particularly useful for adding behavior that occurs before or after saving an object, such as emailing a confirmation, or logging the update.\n\n\ndef perform_update(self, serializer):\n instance = serializer.save()\n send_email_confirmation(user=self.request.user, modified=instance)\n\n\n\nYou can also use these hooks to provide additional validation, by raising a \nValidationError()\n. This can be useful if you need some validation logic to apply at the point of database save. For example:\n\n\ndef perform_create(self, serializer):\n queryset = SignupRequest.objects.filter(user=self.request.user)\n if queryset.exists():\n raise ValidationError('You have already signed up')\n serializer.save(user=self.request.user)\n\n\n\nNote\n: These methods replace the old-style version 2.x \npre_save\n, \npost_save\n, \npre_delete\n and \npost_delete\n methods, which are no longer available.\n\n\nOther methods\n:\n\n\nYou won't typically need to override the following methods, although you might need to call into them if you're writing custom views using \nGenericAPIView\n.\n\n\n\n\nget_serializer_context(self)\n - Returns a dictionary containing any extra context that should be supplied to the serializer. Defaults to including \n'request'\n, \n'view'\n and \n'format'\n keys.\n\n\nget_serializer(self, instance=None, data=None, many=False, partial=False)\n - Returns a serializer instance.\n\n\nget_paginated_response(self, data)\n - Returns a paginated style \nResponse\n object.\n\n\npaginate_queryset(self, queryset)\n - Paginate a queryset if required, either returning a page object, or \nNone\n if pagination is not configured for this view.\n\n\nfilter_queryset(self, queryset)\n - Given a queryset, filter it with whichever filter backends are in use, returning a new queryset.\n\n\n\n\n\n\nMixins\n\n\nThe mixin classes provide the actions that are used to provide the basic view behavior. Note that the mixin classes provide action methods rather than defining the handler methods, such as \n.get()\n and \n.post()\n, directly. This allows for more flexible composition of behavior.\n\n\nThe mixin classes can be imported from \nrest_framework.mixins\n.\n\n\nListModelMixin\n\n\nProvides a \n.list(request, *args, **kwargs)\n method, that implements listing a queryset.\n\n\nIf the queryset is populated, this returns a \n200 OK\n response, with a serialized representation of the queryset as the body of the response. The response data may optionally be paginated.\n\n\nCreateModelMixin\n\n\nProvides a \n.create(request, *args, **kwargs)\n method, that implements creating and saving a new model instance.\n\n\nIf an object is created this returns a \n201 Created\n response, with a serialized representation of the object as the body of the response. If the representation contains a key named \nurl\n, then the \nLocation\n header of the response will be populated with that value.\n\n\nIf the request data provided for creating the object was invalid, a \n400 Bad Request\n response will be returned, with the error details as the body of the response.\n\n\nRetrieveModelMixin\n\n\nProvides a \n.retrieve(request, *args, **kwargs)\n method, that implements returning an existing model instance in a response.\n\n\nIf an object can be retrieved this returns a \n200 OK\n response, with a serialized representation of the object as the body of the response. Otherwise it will return a \n404 Not Found\n.\n\n\nUpdateModelMixin\n\n\nProvides a \n.update(request, *args, **kwargs)\n method, that implements updating and saving an existing model instance.\n\n\nAlso provides a \n.partial_update(request, *args, **kwargs)\n method, which is similar to the \nupdate\n method, except that all fields for the update will be optional. This allows support for HTTP \nPATCH\n requests.\n\n\nIf an object is updated this returns a \n200 OK\n response, with a serialized representation of the object as the body of the response.\n\n\nIf the request data provided for updating the object was invalid, a \n400 Bad Request\n response will be returned, with the error details as the body of the response.\n\n\nDestroyModelMixin\n\n\nProvides a \n.destroy(request, *args, **kwargs)\n method, that implements deletion of an existing model instance.\n\n\nIf an object is deleted this returns a \n204 No Content\n response, otherwise it will return a \n404 Not Found\n.\n\n\n\n\nConcrete View Classes\n\n\nThe following classes are the concrete generic views. If you're using generic views this is normally the level you'll be working at unless you need heavily customized behavior.\n\n\nThe view classes can be imported from \nrest_framework.generics\n.\n\n\nCreateAPIView\n\n\nUsed for \ncreate-only\n endpoints.\n\n\nProvides a \npost\n method handler.\n\n\nExtends: \nGenericAPIView\n, \nCreateModelMixin\n\n\nListAPIView\n\n\nUsed for \nread-only\n endpoints to represent a \ncollection of model instances\n.\n\n\nProvides a \nget\n method handler.\n\n\nExtends: \nGenericAPIView\n, \nListModelMixin\n\n\nRetrieveAPIView\n\n\nUsed for \nread-only\n endpoints to represent a \nsingle model instance\n.\n\n\nProvides a \nget\n method handler.\n\n\nExtends: \nGenericAPIView\n, \nRetrieveModelMixin\n\n\nDestroyAPIView\n\n\nUsed for \ndelete-only\n endpoints for a \nsingle model instance\n.\n\n\nProvides a \ndelete\n method handler.\n\n\nExtends: \nGenericAPIView\n, \nDestroyModelMixin\n\n\nUpdateAPIView\n\n\nUsed for \nupdate-only\n endpoints for a \nsingle model instance\n.\n\n\nProvides \nput\n and \npatch\n method handlers.\n\n\nExtends: \nGenericAPIView\n, \nUpdateModelMixin\n\n\nListCreateAPIView\n\n\nUsed for \nread-write\n endpoints to represent a \ncollection of model instances\n.\n\n\nProvides \nget\n and \npost\n method handlers.\n\n\nExtends: \nGenericAPIView\n, \nListModelMixin\n, \nCreateModelMixin\n\n\nRetrieveUpdateAPIView\n\n\nUsed for \nread or update\n endpoints to represent a \nsingle model instance\n.\n\n\nProvides \nget\n, \nput\n and \npatch\n method handlers.\n\n\nExtends: \nGenericAPIView\n, \nRetrieveModelMixin\n, \nUpdateModelMixin\n\n\nRetrieveDestroyAPIView\n\n\nUsed for \nread or delete\n endpoints to represent a \nsingle model instance\n.\n\n\nProvides \nget\n and \ndelete\n method handlers.\n\n\nExtends: \nGenericAPIView\n, \nRetrieveModelMixin\n, \nDestroyModelMixin\n\n\nRetrieveUpdateDestroyAPIView\n\n\nUsed for \nread-write-delete\n endpoints to represent a \nsingle model instance\n.\n\n\nProvides \nget\n, \nput\n, \npatch\n and \ndelete\n method handlers.\n\n\nExtends: \nGenericAPIView\n, \nRetrieveModelMixin\n, \nUpdateModelMixin\n, \nDestroyModelMixin\n\n\n\n\nCustomizing the generic views\n\n\nOften you'll want to use the existing generic views, but use some slightly customized behavior. If you find yourself reusing some bit of customized behavior in multiple places, you might want to refactor the behavior into a common class that you can then just apply to any view or viewset as needed.\n\n\nCreating custom mixins\n\n\nFor example, if you need to lookup objects based on multiple fields in the URL conf, you could create a mixin class like the following:\n\n\nclass MultipleFieldLookupMixin(object):\n \"\"\"\n Apply this mixin to any view or viewset to get multiple field filtering\n based on a `lookup_fields` attribute, instead of the default single field filtering.\n \"\"\"\n def get_object(self):\n queryset = self.get_queryset() # Get the base queryset\n queryset = self.filter_queryset(queryset) # Apply any filter backends\n filter = {}\n for field in self.lookup_fields:\n if self.kwargs[field]: # Ignore empty fields.\n filter[field] = self.kwargs[field]\n obj = get_object_or_404(queryset, **filter) # Lookup the object\n self.check_object_permissions(self.request, obj)\n return obj\n\n\n\nYou can then simply apply this mixin to a view or viewset anytime you need to apply the custom behavior.\n\n\nclass RetrieveUserView(MultipleFieldLookupMixin, generics.RetrieveAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n lookup_fields = ('account', 'username')\n\n\n\nUsing custom mixins is a good option if you have custom behavior that needs to be used.\n\n\nCreating custom base classes\n\n\nIf you are using a mixin across multiple views, you can take this a step further and create your own set of base views that can then be used throughout your project. For example:\n\n\nclass BaseRetrieveView(MultipleFieldLookupMixin,\n generics.RetrieveAPIView):\n pass\n\nclass BaseRetrieveUpdateDestroyView(MultipleFieldLookupMixin,\n generics.RetrieveUpdateDestroyAPIView):\n pass\n\n\n\nUsing custom base classes is a good option if you have custom behavior that consistently needs to be repeated across a large number of views throughout your project.\n\n\n\n\nPUT as create\n\n\nPrior to version 3.0 the REST framework mixins treated \nPUT\n as either an update or a create operation, depending on if the object already existed or not.\n\n\nAllowing \nPUT\n as create operations is problematic, as it necessarily exposes information about the existence or non-existence of objects. It's also not obvious that transparently allowing re-creating of previously deleted instances is necessarily a better default behavior than simply returning \n404\n responses.\n\n\nBoth styles \"\nPUT\n as 404\" and \"\nPUT\n as create\" can be valid in different circumstances, but from version 3.0 onwards we now use 404 behavior as the default, due to it being simpler and more obvious.\n\n\nIf you need to generic PUT-as-create behavior you may want to include something like \nthis \nAllowPUTAsCreateMixin\n class\n as a mixin to your views.\n\n\n\n\nThird party packages\n\n\nThe following third party packages provide additional generic view implementations.\n\n\nDjango REST Framework bulk\n\n\nThe \ndjango-rest-framework-bulk package\n implements generic view mixins as well as some common concrete generic views to allow to apply bulk operations via API requests.\n\n\nDjango Rest Multiple Models\n\n\nDjango Rest Multiple Models\n provides a generic view (and mixin) for sending multiple serialized models and/or querysets via a single API request.",
+ "location": "/api-guide/generic-views/",
+ "text": "Generic views\n\n\n\n\nDjango\u2019s generic views... were developed as a shortcut for common usage patterns... They take certain common idioms and patterns found in view development and abstract them so that you can quickly write common views of data without having to repeat yourself.\n\n\n \nDjango Documentation\n\n\n\n\nOne 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.\n\n\nThe generic views provided by REST framework allow you to quickly build API views that map closely to your database models.\n\n\nIf the generic views don't suit the needs of your API, you can drop down to using the regular \nAPIView\n class, or reuse the mixins and base classes used by the generic views to compose your own set of reusable generic views.\n\n\nExamples\n\n\nTypically when using the generic views, you'll override the view, and set several class attributes.\n\n\nfrom django.contrib.auth.models import User\nfrom myapp.serializers import UserSerializer\nfrom rest_framework import generics\nfrom rest_framework.permissions import IsAdminUser\n\nclass UserList(generics.ListCreateAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n permission_classes = (IsAdminUser,)\n\n\n\nFor more complex cases you might also want to override various methods on the view class. For example.\n\n\nclass UserList(generics.ListCreateAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n permission_classes = (IsAdminUser,)\n\n def list(self, request):\n # Note the use of `get_queryset()` instead of `self.queryset`\n queryset = self.get_queryset()\n serializer = UserSerializer(queryset, many=True)\n return Response(serializer.data)\n\n\n\nFor very simple cases you might want to pass through any class attributes using the \n.as_view()\n method. For example, your URLconf might include something like the following entry:\n\n\nurl(r'^/users/', ListCreateAPIView.as_view(queryset=User.objects.all(), serializer_class=UserSerializer), name='user-list')\n\n\n\n\n\nAPI Reference\n\n\nGenericAPIView\n\n\nThis class extends REST framework's \nAPIView\n class, adding commonly required behavior for standard list and detail views.\n\n\nEach of the concrete generic views provided is built by combining \nGenericAPIView\n, with one or more mixin classes.\n\n\nAttributes\n\n\nBasic settings\n:\n\n\nThe following attributes control the basic view behavior.\n\n\n\n\nqueryset\n - The queryset that should be used for returning objects from this view. Typically, you must either set this attribute, or override the \nget_queryset()\n method. If you are overriding a view method, it is important that you call \nget_queryset()\n instead of accessing this property directly, as \nqueryset\n will get evaluated once, and those results will be cached for all subsequent requests.\n\n\nserializer_class\n - The serializer class that should be used for validating and deserializing input, and for serializing output. Typically, you must either set this attribute, or override the \nget_serializer_class()\n method.\n\n\nlookup_field\n - The model field that should be used to for performing object lookup of individual model instances. Defaults to \n'pk'\n. Note that when using hyperlinked APIs you'll need to ensure that \nboth\n the API views \nand\n the serializer classes set the lookup fields if you need to use a custom value.\n\n\nlookup_url_kwarg\n - The URL keyword argument that should be used for object lookup. The URL conf should include a keyword argument corresponding to this value. If unset this defaults to using the same value as \nlookup_field\n.\n\n\n\n\nPagination\n:\n\n\nThe following attributes are used to control pagination when used with list views.\n\n\n\n\npagination_class\n - The pagination class that should be used when paginating list results. Defaults to the same value as the \nDEFAULT_PAGINATION_CLASS\n setting, which is \n'rest_framework.pagination.PageNumberPagination'\n. Setting \npagination_class=None\n will disable pagination on this view.\n\n\n\n\nFiltering\n:\n\n\n\n\nfilter_backends\n - A list of filter backend classes that should be used for filtering the queryset. Defaults to the same value as the \nDEFAULT_FILTER_BACKENDS\n setting.\n\n\n\n\nMethods\n\n\nBase methods\n:\n\n\nget_queryset(self)\n\n\nReturns the queryset that should be used for list views, and that should be used as the base for lookups in detail views. Defaults to returning the queryset specified by the \nqueryset\n attribute.\n\n\nThis method should always be used rather than accessing \nself.queryset\n directly, as \nself.queryset\n gets evaluated only once, and those results are cached for all subsequent requests.\n\n\nMay be overridden to provide dynamic behavior, such as returning a queryset, that is specific to the user making the request.\n\n\nFor example:\n\n\ndef get_queryset(self):\n user = self.request.user\n return user.accounts.all()\n\n\n\nget_object(self)\n\n\nReturns an object instance that should be used for detail views. Defaults to using the \nlookup_field\n parameter to filter the base queryset.\n\n\nMay be overridden to provide more complex behavior, such as object lookups based on more than one URL kwarg.\n\n\nFor example:\n\n\ndef get_object(self):\n queryset = self.get_queryset()\n filter = {}\n for field in self.multiple_lookup_fields:\n filter[field] = self.kwargs[field]\n\n obj = get_object_or_404(queryset, **filter)\n self.check_object_permissions(self.request, obj)\n return obj\n\n\n\nNote that if your API doesn't include any object level permissions, you may optionally exclude the \nself.check_object_permissions\n, and simply return the object from the \nget_object_or_404\n lookup.\n\n\nfilter_queryset(self, queryset)\n\n\nGiven a queryset, filter it with whichever filter backends are in use, returning a new queryset.\n\n\nFor example:\n\n\ndef filter_queryset(self, queryset):\n filter_backends = (CategoryFilter,)\n\n if 'geo_route' in self.request.query_params:\n filter_backends = (GeoRouteFilter, CategoryFilter)\n elif 'geo_point' in self.request.query_params:\n filter_backends = (GeoPointFilter, CategoryFilter)\n\n for backend in list(filter_backends):\n queryset = backend().filter_queryset(self.request, queryset, view=self)\n\n return queryset\n\n\n\nget_serializer_class(self)\n\n\nReturns the class that should be used for the serializer. Defaults to returning the \nserializer_class\n attribute.\n\n\nMay be overridden to provide dynamic behavior, such as using different serializers for read and write operations, or providing different serializers to different types of users.\n\n\nFor example:\n\n\ndef get_serializer_class(self):\n if self.request.user.is_staff:\n return FullAccountSerializer\n return BasicAccountSerializer\n\n\n\nSave and deletion hooks\n:\n\n\nThe following methods are provided by the mixin classes, and provide easy overriding of the object save or deletion behavior.\n\n\n\n\nperform_create(self, serializer)\n - Called by \nCreateModelMixin\n when saving a new object instance.\n\n\nperform_update(self, serializer)\n - Called by \nUpdateModelMixin\n when saving an existing object instance.\n\n\nperform_destroy(self, instance)\n - Called by \nDestroyModelMixin\n when deleting an object instance.\n\n\n\n\nThese hooks are particularly useful for setting attributes that are implicit in the request, but are not part of the request data. For instance, you might set an attribute on the object based on the request user, or based on a URL keyword argument.\n\n\ndef perform_create(self, serializer):\n serializer.save(user=self.request.user)\n\n\n\nThese override points are also particularly useful for adding behavior that occurs before or after saving an object, such as emailing a confirmation, or logging the update.\n\n\ndef perform_update(self, serializer):\n instance = serializer.save()\n send_email_confirmation(user=self.request.user, modified=instance)\n\n\n\nYou can also use these hooks to provide additional validation, by raising a \nValidationError()\n. This can be useful if you need some validation logic to apply at the point of database save. For example:\n\n\ndef perform_create(self, serializer):\n queryset = SignupRequest.objects.filter(user=self.request.user)\n if queryset.exists():\n raise ValidationError('You have already signed up')\n serializer.save(user=self.request.user)\n\n\n\nNote\n: These methods replace the old-style version 2.x \npre_save\n, \npost_save\n, \npre_delete\n and \npost_delete\n methods, which are no longer available.\n\n\nOther methods\n:\n\n\nYou won't typically need to override the following methods, although you might need to call into them if you're writing custom views using \nGenericAPIView\n.\n\n\n\n\nget_serializer_context(self)\n - Returns a dictionary containing any extra context that should be supplied to the serializer. Defaults to including \n'request'\n, \n'view'\n and \n'format'\n keys.\n\n\nget_serializer(self, instance=None, data=None, many=False, partial=False)\n - Returns a serializer instance.\n\n\nget_paginated_response(self, data)\n - Returns a paginated style \nResponse\n object.\n\n\npaginate_queryset(self, queryset)\n - Paginate a queryset if required, either returning a page object, or \nNone\n if pagination is not configured for this view.\n\n\nfilter_queryset(self, queryset)\n - Given a queryset, filter it with whichever filter backends are in use, returning a new queryset.\n\n\n\n\n\n\nMixins\n\n\nThe mixin classes provide the actions that are used to provide the basic view behavior. Note that the mixin classes provide action methods rather than defining the handler methods, such as \n.get()\n and \n.post()\n, directly. This allows for more flexible composition of behavior.\n\n\nThe mixin classes can be imported from \nrest_framework.mixins\n.\n\n\nListModelMixin\n\n\nProvides a \n.list(request, *args, **kwargs)\n method, that implements listing a queryset.\n\n\nIf the queryset is populated, this returns a \n200 OK\n response, with a serialized representation of the queryset as the body of the response. The response data may optionally be paginated.\n\n\nCreateModelMixin\n\n\nProvides a \n.create(request, *args, **kwargs)\n method, that implements creating and saving a new model instance.\n\n\nIf an object is created this returns a \n201 Created\n response, with a serialized representation of the object as the body of the response. If the representation contains a key named \nurl\n, then the \nLocation\n header of the response will be populated with that value.\n\n\nIf the request data provided for creating the object was invalid, a \n400 Bad Request\n response will be returned, with the error details as the body of the response.\n\n\nRetrieveModelMixin\n\n\nProvides a \n.retrieve(request, *args, **kwargs)\n method, that implements returning an existing model instance in a response.\n\n\nIf an object can be retrieved this returns a \n200 OK\n response, with a serialized representation of the object as the body of the response. Otherwise it will return a \n404 Not Found\n.\n\n\nUpdateModelMixin\n\n\nProvides a \n.update(request, *args, **kwargs)\n method, that implements updating and saving an existing model instance.\n\n\nAlso provides a \n.partial_update(request, *args, **kwargs)\n method, which is similar to the \nupdate\n method, except that all fields for the update will be optional. This allows support for HTTP \nPATCH\n requests.\n\n\nIf an object is updated this returns a \n200 OK\n response, with a serialized representation of the object as the body of the response.\n\n\nIf the request data provided for updating the object was invalid, a \n400 Bad Request\n response will be returned, with the error details as the body of the response.\n\n\nDestroyModelMixin\n\n\nProvides a \n.destroy(request, *args, **kwargs)\n method, that implements deletion of an existing model instance.\n\n\nIf an object is deleted this returns a \n204 No Content\n response, otherwise it will return a \n404 Not Found\n.\n\n\n\n\nConcrete View Classes\n\n\nThe following classes are the concrete generic views. If you're using generic views this is normally the level you'll be working at unless you need heavily customized behavior.\n\n\nThe view classes can be imported from \nrest_framework.generics\n.\n\n\nCreateAPIView\n\n\nUsed for \ncreate-only\n endpoints.\n\n\nProvides a \npost\n method handler.\n\n\nExtends: \nGenericAPIView\n, \nCreateModelMixin\n\n\nListAPIView\n\n\nUsed for \nread-only\n endpoints to represent a \ncollection of model instances\n.\n\n\nProvides a \nget\n method handler.\n\n\nExtends: \nGenericAPIView\n, \nListModelMixin\n\n\nRetrieveAPIView\n\n\nUsed for \nread-only\n endpoints to represent a \nsingle model instance\n.\n\n\nProvides a \nget\n method handler.\n\n\nExtends: \nGenericAPIView\n, \nRetrieveModelMixin\n\n\nDestroyAPIView\n\n\nUsed for \ndelete-only\n endpoints for a \nsingle model instance\n.\n\n\nProvides a \ndelete\n method handler.\n\n\nExtends: \nGenericAPIView\n, \nDestroyModelMixin\n\n\nUpdateAPIView\n\n\nUsed for \nupdate-only\n endpoints for a \nsingle model instance\n.\n\n\nProvides \nput\n and \npatch\n method handlers.\n\n\nExtends: \nGenericAPIView\n, \nUpdateModelMixin\n\n\nListCreateAPIView\n\n\nUsed for \nread-write\n endpoints to represent a \ncollection of model instances\n.\n\n\nProvides \nget\n and \npost\n method handlers.\n\n\nExtends: \nGenericAPIView\n, \nListModelMixin\n, \nCreateModelMixin\n\n\nRetrieveUpdateAPIView\n\n\nUsed for \nread or update\n endpoints to represent a \nsingle model instance\n.\n\n\nProvides \nget\n, \nput\n and \npatch\n method handlers.\n\n\nExtends: \nGenericAPIView\n, \nRetrieveModelMixin\n, \nUpdateModelMixin\n\n\nRetrieveDestroyAPIView\n\n\nUsed for \nread or delete\n endpoints to represent a \nsingle model instance\n.\n\n\nProvides \nget\n and \ndelete\n method handlers.\n\n\nExtends: \nGenericAPIView\n, \nRetrieveModelMixin\n, \nDestroyModelMixin\n\n\nRetrieveUpdateDestroyAPIView\n\n\nUsed for \nread-write-delete\n endpoints to represent a \nsingle model instance\n.\n\n\nProvides \nget\n, \nput\n, \npatch\n and \ndelete\n method handlers.\n\n\nExtends: \nGenericAPIView\n, \nRetrieveModelMixin\n, \nUpdateModelMixin\n, \nDestroyModelMixin\n\n\n\n\nCustomizing the generic views\n\n\nOften you'll want to use the existing generic views, but use some slightly customized behavior. If you find yourself reusing some bit of customized behavior in multiple places, you might want to refactor the behavior into a common class that you can then just apply to any view or viewset as needed.\n\n\nCreating custom mixins\n\n\nFor example, if you need to lookup objects based on multiple fields in the URL conf, you could create a mixin class like the following:\n\n\nclass MultipleFieldLookupMixin(object):\n \"\"\"\n Apply this mixin to any view or viewset to get multiple field filtering\n based on a `lookup_fields` attribute, instead of the default single field filtering.\n \"\"\"\n def get_object(self):\n queryset = self.get_queryset() # Get the base queryset\n queryset = self.filter_queryset(queryset) # Apply any filter backends\n filter = {}\n for field in self.lookup_fields:\n if self.kwargs[field]: # Ignore empty fields.\n filter[field] = self.kwargs[field]\n obj = get_object_or_404(queryset, **filter) # Lookup the object\n self.check_object_permissions(self.request, obj)\n return obj\n\n\n\nYou can then simply apply this mixin to a view or viewset anytime you need to apply the custom behavior.\n\n\nclass RetrieveUserView(MultipleFieldLookupMixin, generics.RetrieveAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n lookup_fields = ('account', 'username')\n\n\n\nUsing custom mixins is a good option if you have custom behavior that needs to be used.\n\n\nCreating custom base classes\n\n\nIf you are using a mixin across multiple views, you can take this a step further and create your own set of base views that can then be used throughout your project. For example:\n\n\nclass BaseRetrieveView(MultipleFieldLookupMixin,\n generics.RetrieveAPIView):\n pass\n\nclass BaseRetrieveUpdateDestroyView(MultipleFieldLookupMixin,\n generics.RetrieveUpdateDestroyAPIView):\n pass\n\n\n\nUsing custom base classes is a good option if you have custom behavior that consistently needs to be repeated across a large number of views throughout your project.\n\n\n\n\nPUT as create\n\n\nPrior to version 3.0 the REST framework mixins treated \nPUT\n as either an update or a create operation, depending on if the object already existed or not.\n\n\nAllowing \nPUT\n as create operations is problematic, as it necessarily exposes information about the existence or non-existence of objects. It's also not obvious that transparently allowing re-creating of previously deleted instances is necessarily a better default behavior than simply returning \n404\n responses.\n\n\nBoth styles \"\nPUT\n as 404\" and \"\nPUT\n as create\" can be valid in different circumstances, but from version 3.0 onwards we now use 404 behavior as the default, due to it being simpler and more obvious.\n\n\nIf you need to generic PUT-as-create behavior you may want to include something like \nthis \nAllowPUTAsCreateMixin\n class\n as a mixin to your views.\n\n\n\n\nThird party packages\n\n\nThe following third party packages provide additional generic view implementations.\n\n\nDjango REST Framework bulk\n\n\nThe \ndjango-rest-framework-bulk package\n implements generic view mixins as well as some common concrete generic views to allow to apply bulk operations via API requests.\n\n\nDjango Rest Multiple Models\n\n\nDjango Rest Multiple Models\n provides a generic view (and mixin) for sending multiple serialized models and/or querysets via a single API request.",
"title": "Generic views"
- },
+ },
{
- "location": "/api-guide/generic-views/#generic-views",
- "text": "Django\u2019s generic views... were developed as a shortcut for common usage patterns... They take certain common idioms and patterns found in view development and abstract them so that you can quickly write common views of data without having to repeat yourself. \u2014 Django Documentation 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. If the generic views don't suit the needs of your API, you can drop down to using the regular APIView class, or reuse the mixins and base classes used by the generic views to compose your own set of reusable generic views.",
+ "location": "/api-guide/generic-views/#generic-views",
+ "text": "Django\u2019s generic views... were developed as a shortcut for common usage patterns... They take certain common idioms and patterns found in view development and abstract them so that you can quickly write common views of data without having to repeat yourself. Django Documentation 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. If the generic views don't suit the needs of your API, you can drop down to using the regular APIView class, or reuse the mixins and base classes used by the generic views to compose your own set of reusable generic views.",
"title": "Generic views"
- },
+ },
{
- "location": "/api-guide/generic-views/#examples",
- "text": "Typically when using the generic views, you'll override the view, and set several class attributes. from django.contrib.auth.models import User\nfrom myapp.serializers import UserSerializer\nfrom rest_framework import generics\nfrom rest_framework.permissions import IsAdminUser\n\nclass UserList(generics.ListCreateAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n permission_classes = (IsAdminUser,) For more complex cases you might also want to override various methods on the view class. For example. class UserList(generics.ListCreateAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n permission_classes = (IsAdminUser,)\n\n def list(self, request):\n # Note the use of `get_queryset()` instead of `self.queryset`\n queryset = self.get_queryset()\n serializer = UserSerializer(queryset, many=True)\n return Response(serializer.data) For very simple cases you might want to pass through any class attributes using the .as_view() method. For example, your URLconf might include something like the following entry: url(r'^/users/', ListCreateAPIView.as_view(queryset=User.objects.all(), serializer_class=UserSerializer), name='user-list')",
+ "location": "/api-guide/generic-views/#examples",
+ "text": "Typically when using the generic views, you'll override the view, and set several class attributes. from django.contrib.auth.models import User\nfrom myapp.serializers import UserSerializer\nfrom rest_framework import generics\nfrom rest_framework.permissions import IsAdminUser\n\nclass UserList(generics.ListCreateAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n permission_classes = (IsAdminUser,) For more complex cases you might also want to override various methods on the view class. For example. class UserList(generics.ListCreateAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n permission_classes = (IsAdminUser,)\n\n def list(self, request):\n # Note the use of `get_queryset()` instead of `self.queryset`\n queryset = self.get_queryset()\n serializer = UserSerializer(queryset, many=True)\n return Response(serializer.data) For very simple cases you might want to pass through any class attributes using the .as_view() method. For example, your URLconf might include something like the following entry: url(r'^/users/', ListCreateAPIView.as_view(queryset=User.objects.all(), serializer_class=UserSerializer), name='user-list')",
"title": "Examples"
- },
+ },
{
- "location": "/api-guide/generic-views/#api-reference",
- "text": "",
+ "location": "/api-guide/generic-views/#api-reference",
+ "text": "",
"title": "API Reference"
- },
+ },
{
- "location": "/api-guide/generic-views/#genericapiview",
- "text": "This class extends REST framework's APIView class, adding commonly required behavior for standard list and detail views. Each of the concrete generic views provided is built by combining GenericAPIView , with one or more mixin classes.",
+ "location": "/api-guide/generic-views/#genericapiview",
+ "text": "This class extends REST framework's APIView class, adding commonly required behavior for standard list and detail views. Each of the concrete generic views provided is built by combining GenericAPIView , with one or more mixin classes.",
"title": "GenericAPIView"
- },
+ },
{
- "location": "/api-guide/generic-views/#attributes",
- "text": "Basic settings : The following attributes control the basic view behavior. queryset - The queryset that should be used for returning objects from this view. Typically, you must either set this attribute, or override the get_queryset() method. If you are overriding a view method, it is important that you call get_queryset() instead of accessing this property directly, as queryset will get evaluated once, and those results will be cached for all subsequent requests. serializer_class - The serializer class that should be used for validating and deserializing input, and for serializing output. Typically, you must either set this attribute, or override the get_serializer_class() method. lookup_field - The model field that should be used to for performing object lookup of individual model instances. Defaults to 'pk' . Note that when using hyperlinked APIs you'll need to ensure that both the API views and the serializer classes set the lookup fields if you need to use a custom value. lookup_url_kwarg - The URL keyword argument that should be used for object lookup. The URL conf should include a keyword argument corresponding to this value. If unset this defaults to using the same value as lookup_field . Pagination : The following attributes are used to control pagination when used with list views. 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' . Setting pagination_class=None will disable pagination on this view. 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.",
+ "location": "/api-guide/generic-views/#attributes",
+ "text": "Basic settings : The following attributes control the basic view behavior. queryset - The queryset that should be used for returning objects from this view. Typically, you must either set this attribute, or override the get_queryset() method. If you are overriding a view method, it is important that you call get_queryset() instead of accessing this property directly, as queryset will get evaluated once, and those results will be cached for all subsequent requests. serializer_class - The serializer class that should be used for validating and deserializing input, and for serializing output. Typically, you must either set this attribute, or override the get_serializer_class() method. lookup_field - The model field that should be used to for performing object lookup of individual model instances. Defaults to 'pk' . Note that when using hyperlinked APIs you'll need to ensure that both the API views and the serializer classes set the lookup fields if you need to use a custom value. lookup_url_kwarg - The URL keyword argument that should be used for object lookup. The URL conf should include a keyword argument corresponding to this value. If unset this defaults to using the same value as lookup_field . Pagination : The following attributes are used to control pagination when used with list views. 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' . Setting pagination_class=None will disable pagination on this view. 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.",
"title": "Attributes"
- },
+ },
{
- "location": "/api-guide/generic-views/#methods",
- "text": "Base methods :",
+ "location": "/api-guide/generic-views/#methods",
+ "text": "Base methods :",
"title": "Methods"
- },
+ },
{
- "location": "/api-guide/generic-views/#get_querysetself",
- "text": "Returns the queryset that should be used for list views, and that should be used as the base for lookups in detail views. Defaults to returning the queryset specified by the queryset attribute. This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests. May be overridden to provide dynamic behavior, such as returning a queryset, that is specific to the user making the request. For example: def get_queryset(self):\n user = self.request.user\n return user.accounts.all()",
+ "location": "/api-guide/generic-views/#get_querysetself",
+ "text": "Returns the queryset that should be used for list views, and that should be used as the base for lookups in detail views. Defaults to returning the queryset specified by the queryset attribute. This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests. May be overridden to provide dynamic behavior, such as returning a queryset, that is specific to the user making the request. For example: def get_queryset(self):\n user = self.request.user\n return user.accounts.all()",
"title": "get_queryset(self)"
- },
+ },
{
- "location": "/api-guide/generic-views/#get_objectself",
- "text": "Returns an object instance that should be used for detail views. Defaults to using the lookup_field parameter to filter the base queryset. May be overridden to provide more complex behavior, such as object lookups based on more than one URL kwarg. For example: def get_object(self):\n queryset = self.get_queryset()\n filter = {}\n for field in self.multiple_lookup_fields:\n filter[field] = self.kwargs[field]\n\n obj = get_object_or_404(queryset, **filter)\n self.check_object_permissions(self.request, obj)\n return obj Note that if your API doesn't include any object level permissions, you may optionally exclude the self.check_object_permissions , and simply return the object from the get_object_or_404 lookup.",
+ "location": "/api-guide/generic-views/#get_objectself",
+ "text": "Returns an object instance that should be used for detail views. Defaults to using the lookup_field parameter to filter the base queryset. May be overridden to provide more complex behavior, such as object lookups based on more than one URL kwarg. For example: def get_object(self):\n queryset = self.get_queryset()\n filter = {}\n for field in self.multiple_lookup_fields:\n filter[field] = self.kwargs[field]\n\n obj = get_object_or_404(queryset, **filter)\n self.check_object_permissions(self.request, obj)\n return obj Note that if your API doesn't include any object level permissions, you may optionally exclude the self.check_object_permissions , and simply return the object from the get_object_or_404 lookup.",
"title": "get_object(self)"
- },
+ },
{
- "location": "/api-guide/generic-views/#filter_querysetself-queryset",
- "text": "Given a queryset, filter it with whichever filter backends are in use, returning a new queryset. For example: def filter_queryset(self, queryset):\n filter_backends = (CategoryFilter,)\n\n if 'geo_route' in self.request.query_params:\n filter_backends = (GeoRouteFilter, CategoryFilter)\n elif 'geo_point' in self.request.query_params:\n filter_backends = (GeoPointFilter, CategoryFilter)\n\n for backend in list(filter_backends):\n queryset = backend().filter_queryset(self.request, queryset, view=self)\n\n return queryset",
+ "location": "/api-guide/generic-views/#filter_querysetself-queryset",
+ "text": "Given a queryset, filter it with whichever filter backends are in use, returning a new queryset. For example: def filter_queryset(self, queryset):\n filter_backends = (CategoryFilter,)\n\n if 'geo_route' in self.request.query_params:\n filter_backends = (GeoRouteFilter, CategoryFilter)\n elif 'geo_point' in self.request.query_params:\n filter_backends = (GeoPointFilter, CategoryFilter)\n\n for backend in list(filter_backends):\n queryset = backend().filter_queryset(self.request, queryset, view=self)\n\n return queryset",
"title": "filter_queryset(self, queryset)"
- },
+ },
{
- "location": "/api-guide/generic-views/#get_serializer_classself",
- "text": "Returns the class that should be used for the serializer. Defaults to returning the serializer_class attribute. May be overridden to provide dynamic behavior, such as using different serializers for read and write operations, or providing different serializers to different types of users. For example: def get_serializer_class(self):\n if self.request.user.is_staff:\n return FullAccountSerializer\n return BasicAccountSerializer Save and deletion hooks : The following methods are provided by the mixin classes, and provide easy overriding of the object save or deletion behavior. perform_create(self, serializer) - Called by CreateModelMixin when saving a new object instance. perform_update(self, serializer) - Called by UpdateModelMixin when saving an existing object instance. perform_destroy(self, instance) - Called by DestroyModelMixin when deleting an object instance. These hooks are particularly useful for setting attributes that are implicit in the request, but are not part of the request data. For instance, you might set an attribute on the object based on the request user, or based on a URL keyword argument. def perform_create(self, serializer):\n serializer.save(user=self.request.user) These override points are also particularly useful for adding behavior that occurs before or after saving an object, such as emailing a confirmation, or logging the update. def perform_update(self, serializer):\n instance = serializer.save()\n send_email_confirmation(user=self.request.user, modified=instance) You can also use these hooks to provide additional validation, by raising a ValidationError() . This can be useful if you need some validation logic to apply at the point of database save. For example: def perform_create(self, serializer):\n queryset = SignupRequest.objects.filter(user=self.request.user)\n if queryset.exists():\n raise ValidationError('You have already signed up')\n serializer.save(user=self.request.user) Note : These methods replace the old-style version 2.x pre_save , post_save , pre_delete and post_delete methods, which are no longer available. Other methods : You won't typically need to override the following methods, although you might need to call into them if you're writing custom views using GenericAPIView . get_serializer_context(self) - Returns a dictionary containing any extra context that should be supplied to the serializer. Defaults to including 'request' , 'view' and 'format' keys. get_serializer(self, instance=None, data=None, many=False, partial=False) - Returns a serializer instance. get_paginated_response(self, data) - Returns a paginated style Response object. paginate_queryset(self, queryset) - Paginate a queryset if required, either returning a page object, or None if pagination is not configured for this view. filter_queryset(self, queryset) - Given a queryset, filter it with whichever filter backends are in use, returning a new queryset.",
+ "location": "/api-guide/generic-views/#get_serializer_classself",
+ "text": "Returns the class that should be used for the serializer. Defaults to returning the serializer_class attribute. May be overridden to provide dynamic behavior, such as using different serializers for read and write operations, or providing different serializers to different types of users. For example: def get_serializer_class(self):\n if self.request.user.is_staff:\n return FullAccountSerializer\n return BasicAccountSerializer Save and deletion hooks : The following methods are provided by the mixin classes, and provide easy overriding of the object save or deletion behavior. perform_create(self, serializer) - Called by CreateModelMixin when saving a new object instance. perform_update(self, serializer) - Called by UpdateModelMixin when saving an existing object instance. perform_destroy(self, instance) - Called by DestroyModelMixin when deleting an object instance. These hooks are particularly useful for setting attributes that are implicit in the request, but are not part of the request data. For instance, you might set an attribute on the object based on the request user, or based on a URL keyword argument. def perform_create(self, serializer):\n serializer.save(user=self.request.user) These override points are also particularly useful for adding behavior that occurs before or after saving an object, such as emailing a confirmation, or logging the update. def perform_update(self, serializer):\n instance = serializer.save()\n send_email_confirmation(user=self.request.user, modified=instance) You can also use these hooks to provide additional validation, by raising a ValidationError() . This can be useful if you need some validation logic to apply at the point of database save. For example: def perform_create(self, serializer):\n queryset = SignupRequest.objects.filter(user=self.request.user)\n if queryset.exists():\n raise ValidationError('You have already signed up')\n serializer.save(user=self.request.user) Note : These methods replace the old-style version 2.x pre_save , post_save , pre_delete and post_delete methods, which are no longer available. Other methods : You won't typically need to override the following methods, although you might need to call into them if you're writing custom views using GenericAPIView . get_serializer_context(self) - Returns a dictionary containing any extra context that should be supplied to the serializer. Defaults to including 'request' , 'view' and 'format' keys. get_serializer(self, instance=None, data=None, many=False, partial=False) - Returns a serializer instance. get_paginated_response(self, data) - Returns a paginated style Response object. paginate_queryset(self, queryset) - Paginate a queryset if required, either returning a page object, or None if pagination is not configured for this view. filter_queryset(self, queryset) - Given a queryset, filter it with whichever filter backends are in use, returning a new queryset.",
"title": "get_serializer_class(self)"
- },
+ },
{
- "location": "/api-guide/generic-views/#mixins",
- "text": "The mixin classes provide the actions that are used to provide the basic view behavior. Note that the mixin classes provide action methods rather than defining the handler methods, such as .get() and .post() , directly. This allows for more flexible composition of behavior. The mixin classes can be imported from rest_framework.mixins .",
+ "location": "/api-guide/generic-views/#mixins",
+ "text": "The mixin classes provide the actions that are used to provide the basic view behavior. Note that the mixin classes provide action methods rather than defining the handler methods, such as .get() and .post() , directly. This allows for more flexible composition of behavior. The mixin classes can be imported from rest_framework.mixins .",
"title": "Mixins"
- },
+ },
{
- "location": "/api-guide/generic-views/#listmodelmixin",
- "text": "Provides a .list(request, *args, **kwargs) method, that implements listing a queryset. If the queryset is populated, this returns a 200 OK response, with a serialized representation of the queryset as the body of the response. The response data may optionally be paginated.",
+ "location": "/api-guide/generic-views/#listmodelmixin",
+ "text": "Provides a .list(request, *args, **kwargs) method, that implements listing a queryset. If the queryset is populated, this returns a 200 OK response, with a serialized representation of the queryset as the body of the response. The response data may optionally be paginated.",
"title": "ListModelMixin"
- },
+ },
{
- "location": "/api-guide/generic-views/#createmodelmixin",
- "text": "Provides a .create(request, *args, **kwargs) method, that implements creating and saving a new model instance. If an object is created this returns a 201 Created response, with a serialized representation of the object as the body of the response. If the representation contains a key named url , then the Location header of the response will be populated with that value. If the request data provided for creating the object was invalid, a 400 Bad Request response will be returned, with the error details as the body of the response.",
+ "location": "/api-guide/generic-views/#createmodelmixin",
+ "text": "Provides a .create(request, *args, **kwargs) method, that implements creating and saving a new model instance. If an object is created this returns a 201 Created response, with a serialized representation of the object as the body of the response. If the representation contains a key named url , then the Location header of the response will be populated with that value. If the request data provided for creating the object was invalid, a 400 Bad Request response will be returned, with the error details as the body of the response.",
"title": "CreateModelMixin"
- },
+ },
{
- "location": "/api-guide/generic-views/#retrievemodelmixin",
- "text": "Provides a .retrieve(request, *args, **kwargs) method, that implements returning an existing model instance in a response. If an object can be retrieved this returns a 200 OK response, with a serialized representation of the object as the body of the response. Otherwise it will return a 404 Not Found .",
+ "location": "/api-guide/generic-views/#retrievemodelmixin",
+ "text": "Provides a .retrieve(request, *args, **kwargs) method, that implements returning an existing model instance in a response. If an object can be retrieved this returns a 200 OK response, with a serialized representation of the object as the body of the response. Otherwise it will return a 404 Not Found .",
"title": "RetrieveModelMixin"
- },
+ },
{
- "location": "/api-guide/generic-views/#updatemodelmixin",
- "text": "Provides a .update(request, *args, **kwargs) method, that implements updating and saving an existing model instance. Also provides a .partial_update(request, *args, **kwargs) method, which is similar to the update method, except that all fields for the update will be optional. This allows support for HTTP PATCH requests. 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 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.",
+ "location": "/api-guide/generic-views/#updatemodelmixin",
+ "text": "Provides a .update(request, *args, **kwargs) method, that implements updating and saving an existing model instance. Also provides a .partial_update(request, *args, **kwargs) method, which is similar to the update method, except that all fields for the update will be optional. This allows support for HTTP PATCH requests. 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 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.",
"title": "UpdateModelMixin"
- },
+ },
{
- "location": "/api-guide/generic-views/#destroymodelmixin",
- "text": "Provides a .destroy(request, *args, **kwargs) method, that implements deletion of an existing model instance. If an object is deleted this returns a 204 No Content response, otherwise it will return a 404 Not Found .",
+ "location": "/api-guide/generic-views/#destroymodelmixin",
+ "text": "Provides a .destroy(request, *args, **kwargs) method, that implements deletion of an existing model instance. If an object is deleted this returns a 204 No Content response, otherwise it will return a 404 Not Found .",
"title": "DestroyModelMixin"
- },
+ },
{
- "location": "/api-guide/generic-views/#concrete-view-classes",
- "text": "The following classes are the concrete generic views. If you're using generic views this is normally the level you'll be working at unless you need heavily customized behavior. The view classes can be imported from rest_framework.generics .",
+ "location": "/api-guide/generic-views/#concrete-view-classes",
+ "text": "The following classes are the concrete generic views. If you're using generic views this is normally the level you'll be working at unless you need heavily customized behavior. The view classes can be imported from rest_framework.generics .",
"title": "Concrete View Classes"
- },
+ },
{
- "location": "/api-guide/generic-views/#createapiview",
- "text": "Used for create-only endpoints. Provides a post method handler. Extends: GenericAPIView , CreateModelMixin",
+ "location": "/api-guide/generic-views/#createapiview",
+ "text": "Used for create-only endpoints. Provides a post method handler. Extends: GenericAPIView , CreateModelMixin",
"title": "CreateAPIView"
- },
+ },
{
- "location": "/api-guide/generic-views/#listapiview",
- "text": "Used for read-only endpoints to represent a collection of model instances . Provides a get method handler. Extends: GenericAPIView , ListModelMixin",
+ "location": "/api-guide/generic-views/#listapiview",
+ "text": "Used for read-only endpoints to represent a collection of model instances . Provides a get method handler. Extends: GenericAPIView , ListModelMixin",
"title": "ListAPIView"
- },
+ },
{
- "location": "/api-guide/generic-views/#retrieveapiview",
- "text": "Used for read-only endpoints to represent a single model instance . Provides a get method handler. Extends: GenericAPIView , RetrieveModelMixin",
+ "location": "/api-guide/generic-views/#retrieveapiview",
+ "text": "Used for read-only endpoints to represent a single model instance . Provides a get method handler. Extends: GenericAPIView , RetrieveModelMixin",
"title": "RetrieveAPIView"
- },
+ },
{
- "location": "/api-guide/generic-views/#destroyapiview",
- "text": "Used for delete-only endpoints for a single model instance . Provides a delete method handler. Extends: GenericAPIView , DestroyModelMixin",
+ "location": "/api-guide/generic-views/#destroyapiview",
+ "text": "Used for delete-only endpoints for a single model instance . Provides a delete method handler. Extends: GenericAPIView , DestroyModelMixin",
"title": "DestroyAPIView"
- },
+ },
{
- "location": "/api-guide/generic-views/#updateapiview",
- "text": "Used for update-only endpoints for a single model instance . Provides put and patch method handlers. Extends: GenericAPIView , UpdateModelMixin",
+ "location": "/api-guide/generic-views/#updateapiview",
+ "text": "Used for update-only endpoints for a single model instance . Provides put and patch method handlers. Extends: GenericAPIView , UpdateModelMixin",
"title": "UpdateAPIView"
- },
+ },
{
- "location": "/api-guide/generic-views/#listcreateapiview",
- "text": "Used for read-write endpoints to represent a collection of model instances . Provides get and post method handlers. Extends: GenericAPIView , ListModelMixin , CreateModelMixin",
+ "location": "/api-guide/generic-views/#listcreateapiview",
+ "text": "Used for read-write endpoints to represent a collection of model instances . Provides get and post method handlers. Extends: GenericAPIView , ListModelMixin , CreateModelMixin",
"title": "ListCreateAPIView"
- },
+ },
{
- "location": "/api-guide/generic-views/#retrieveupdateapiview",
- "text": "Used for read or update endpoints to represent a single model instance . Provides get , put and patch method handlers. Extends: GenericAPIView , RetrieveModelMixin , UpdateModelMixin",
+ "location": "/api-guide/generic-views/#retrieveupdateapiview",
+ "text": "Used for read or update endpoints to represent a single model instance . Provides get , put and patch method handlers. Extends: GenericAPIView , RetrieveModelMixin , UpdateModelMixin",
"title": "RetrieveUpdateAPIView"
- },
+ },
{
- "location": "/api-guide/generic-views/#retrievedestroyapiview",
- "text": "Used for read or delete endpoints to represent a single model instance . Provides get and delete method handlers. Extends: GenericAPIView , RetrieveModelMixin , DestroyModelMixin",
+ "location": "/api-guide/generic-views/#retrievedestroyapiview",
+ "text": "Used for read or delete endpoints to represent a single model instance . Provides get and delete method handlers. Extends: GenericAPIView , RetrieveModelMixin , DestroyModelMixin",
"title": "RetrieveDestroyAPIView"
- },
+ },
{
- "location": "/api-guide/generic-views/#retrieveupdatedestroyapiview",
- "text": "Used for read-write-delete endpoints to represent a single model instance . Provides get , put , patch and delete method handlers. Extends: GenericAPIView , RetrieveModelMixin , UpdateModelMixin , DestroyModelMixin",
+ "location": "/api-guide/generic-views/#retrieveupdatedestroyapiview",
+ "text": "Used for read-write-delete endpoints to represent a single model instance . Provides get , put , patch and delete method handlers. Extends: GenericAPIView , RetrieveModelMixin , UpdateModelMixin , DestroyModelMixin",
"title": "RetrieveUpdateDestroyAPIView"
- },
+ },
{
- "location": "/api-guide/generic-views/#customizing-the-generic-views",
- "text": "Often you'll want to use the existing generic views, but use some slightly customized behavior. If you find yourself reusing some bit of customized behavior in multiple places, you might want to refactor the behavior into a common class that you can then just apply to any view or viewset as needed.",
+ "location": "/api-guide/generic-views/#customizing-the-generic-views",
+ "text": "Often you'll want to use the existing generic views, but use some slightly customized behavior. If you find yourself reusing some bit of customized behavior in multiple places, you might want to refactor the behavior into a common class that you can then just apply to any view or viewset as needed.",
"title": "Customizing the generic views"
- },
+ },
{
- "location": "/api-guide/generic-views/#creating-custom-mixins",
- "text": "For example, if you need to lookup objects based on multiple fields in the URL conf, you could create a mixin class like the following: class MultipleFieldLookupMixin(object):\n \"\"\"\n Apply this mixin to any view or viewset to get multiple field filtering\n based on a `lookup_fields` attribute, instead of the default single field filtering.\n \"\"\"\n def get_object(self):\n queryset = self.get_queryset() # Get the base queryset\n queryset = self.filter_queryset(queryset) # Apply any filter backends\n filter = {}\n for field in self.lookup_fields:\n if self.kwargs[field]: # Ignore empty fields.\n filter[field] = self.kwargs[field]\n obj = get_object_or_404(queryset, **filter) # Lookup the object\n self.check_object_permissions(self.request, obj)\n return obj You can then simply apply this mixin to a view or viewset anytime you need to apply the custom behavior. class RetrieveUserView(MultipleFieldLookupMixin, generics.RetrieveAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n lookup_fields = ('account', 'username') Using custom mixins is a good option if you have custom behavior that needs to be used.",
+ "location": "/api-guide/generic-views/#creating-custom-mixins",
+ "text": "For example, if you need to lookup objects based on multiple fields in the URL conf, you could create a mixin class like the following: class MultipleFieldLookupMixin(object):\n \"\"\"\n Apply this mixin to any view or viewset to get multiple field filtering\n based on a `lookup_fields` attribute, instead of the default single field filtering.\n \"\"\"\n def get_object(self):\n queryset = self.get_queryset() # Get the base queryset\n queryset = self.filter_queryset(queryset) # Apply any filter backends\n filter = {}\n for field in self.lookup_fields:\n if self.kwargs[field]: # Ignore empty fields.\n filter[field] = self.kwargs[field]\n obj = get_object_or_404(queryset, **filter) # Lookup the object\n self.check_object_permissions(self.request, obj)\n return obj You can then simply apply this mixin to a view or viewset anytime you need to apply the custom behavior. class RetrieveUserView(MultipleFieldLookupMixin, generics.RetrieveAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n lookup_fields = ('account', 'username') Using custom mixins is a good option if you have custom behavior that needs to be used.",
"title": "Creating custom mixins"
- },
+ },
{
- "location": "/api-guide/generic-views/#creating-custom-base-classes",
- "text": "If you are using a mixin across multiple views, you can take this a step further and create your own set of base views that can then be used throughout your project. For example: class BaseRetrieveView(MultipleFieldLookupMixin,\n generics.RetrieveAPIView):\n pass\n\nclass BaseRetrieveUpdateDestroyView(MultipleFieldLookupMixin,\n generics.RetrieveUpdateDestroyAPIView):\n pass Using custom base classes is a good option if you have custom behavior that consistently needs to be repeated across a large number of views throughout your project.",
+ "location": "/api-guide/generic-views/#creating-custom-base-classes",
+ "text": "If you are using a mixin across multiple views, you can take this a step further and create your own set of base views that can then be used throughout your project. For example: class BaseRetrieveView(MultipleFieldLookupMixin,\n generics.RetrieveAPIView):\n pass\n\nclass BaseRetrieveUpdateDestroyView(MultipleFieldLookupMixin,\n generics.RetrieveUpdateDestroyAPIView):\n pass Using custom base classes is a good option if you have custom behavior that consistently needs to be repeated across a large number of views throughout your project.",
"title": "Creating custom base classes"
- },
+ },
{
- "location": "/api-guide/generic-views/#put-as-create",
- "text": "Prior to version 3.0 the REST framework mixins treated PUT as either an update or a create operation, depending on if the object already existed or not. Allowing PUT as create operations is problematic, as it necessarily exposes information about the existence or non-existence of objects. It's also not obvious that transparently allowing re-creating of previously deleted instances is necessarily a better default behavior than simply returning 404 responses. Both styles \" PUT as 404\" and \" PUT as create\" can be valid in different circumstances, but from version 3.0 onwards we now use 404 behavior as the default, due to it being simpler and more obvious. If you need to generic PUT-as-create behavior you may want to include something like this AllowPUTAsCreateMixin class as a mixin to your views.",
+ "location": "/api-guide/generic-views/#put-as-create",
+ "text": "Prior to version 3.0 the REST framework mixins treated PUT as either an update or a create operation, depending on if the object already existed or not. Allowing PUT as create operations is problematic, as it necessarily exposes information about the existence or non-existence of objects. It's also not obvious that transparently allowing re-creating of previously deleted instances is necessarily a better default behavior than simply returning 404 responses. Both styles \" PUT as 404\" and \" PUT as create\" can be valid in different circumstances, but from version 3.0 onwards we now use 404 behavior as the default, due to it being simpler and more obvious. If you need to generic PUT-as-create behavior you may want to include something like this AllowPUTAsCreateMixin class as a mixin to your views.",
"title": "PUT as create"
- },
+ },
{
- "location": "/api-guide/generic-views/#third-party-packages",
- "text": "The following third party packages provide additional generic view implementations.",
+ "location": "/api-guide/generic-views/#third-party-packages",
+ "text": "The following third party packages provide additional generic view implementations.",
"title": "Third party packages"
- },
+ },
{
- "location": "/api-guide/generic-views/#django-rest-framework-bulk",
- "text": "The django-rest-framework-bulk package implements generic view mixins as well as some common concrete generic views to allow to apply bulk operations via API requests.",
+ "location": "/api-guide/generic-views/#django-rest-framework-bulk",
+ "text": "The django-rest-framework-bulk package implements generic view mixins as well as some common concrete generic views to allow to apply bulk operations via API requests.",
"title": "Django REST Framework bulk"
- },
+ },
{
- "location": "/api-guide/generic-views/#django-rest-multiple-models",
- "text": "Django Rest Multiple Models provides a generic view (and mixin) for sending multiple serialized models and/or querysets via a single API request.",
+ "location": "/api-guide/generic-views/#django-rest-multiple-models",
+ "text": "Django Rest Multiple Models provides a generic view (and mixin) for sending multiple serialized models and/or querysets via a single API request.",
"title": "Django Rest Multiple Models"
- },
+ },
{
- "location": "/api-guide/viewsets/",
- "text": "ViewSets\n\n\n\n\nAfter routing has determined which controller to use for a request, your controller is responsible for making sense of the request and producing the appropriate output.\n\n\n\u2014 \nRuby on Rails Documentation\n\n\n\n\nDjango REST framework allows you to combine the logic for a set of related views in a single class, called a \nViewSet\n. In other frameworks you may also find conceptually similar implementations named something like 'Resources' or 'Controllers'.\n\n\nA \nViewSet\n class is simply \na type of class-based View, that does not provide any method handlers\n such as \n.get()\n or \n.post()\n, and instead provides actions such as \n.list()\n and \n.create()\n.\n\n\nThe method handlers for a \nViewSet\n are only bound to the corresponding actions at the point of finalizing the view, using the \n.as_view()\n method.\n\n\nTypically, rather than explicitly registering the views in a viewset in the urlconf, you'll register the viewset with a router class, that automatically determines the urlconf for you.\n\n\nExample\n\n\nLet's define a simple viewset that can be used to list or retrieve all the users in the system.\n\n\nfrom django.contrib.auth.models import User\nfrom django.shortcuts import get_object_or_404\nfrom myapps.serializers import UserSerializer\nfrom rest_framework import viewsets\nfrom rest_framework.response import Response\n\nclass UserViewSet(viewsets.ViewSet):\n \"\"\"\n A simple ViewSet for listing or retrieving users.\n \"\"\"\n def list(self, request):\n queryset = User.objects.all()\n serializer = UserSerializer(queryset, many=True)\n return Response(serializer.data)\n\n def retrieve(self, request, pk=None):\n queryset = User.objects.all()\n user = get_object_or_404(queryset, pk=pk)\n serializer = UserSerializer(user)\n return Response(serializer.data)\n\n\n\nIf we need to, we can bind this viewset into two separate views, like so:\n\n\nuser_list = UserViewSet.as_view({'get': 'list'})\nuser_detail = UserViewSet.as_view({'get': 'retrieve'})\n\n\n\nTypically we wouldn't do this, but would instead register the viewset with a router, and allow the urlconf to be automatically generated.\n\n\nfrom myapp.views import UserViewSet\nfrom rest_framework.routers import DefaultRouter\n\nrouter = DefaultRouter()\nrouter.register(r'users', UserViewSet, basename='user')\nurlpatterns = router.urls\n\n\n\nRather than writing your own viewsets, you'll often want to use the existing base classes that provide a default set of behavior. For example:\n\n\nclass UserViewSet(viewsets.ModelViewSet):\n \"\"\"\n A viewset for viewing and editing user instances.\n \"\"\"\n serializer_class = UserSerializer\n queryset = User.objects.all()\n\n\n\nThere are two main advantages of using a \nViewSet\n class over using a \nView\n class.\n\n\n\n\nRepeated logic can be combined into a single class. In the above example, we only need to specify the \nqueryset\n once, and it'll be used across multiple views.\n\n\nBy using routers, we no longer need to deal with wiring up the URL conf ourselves.\n\n\n\n\nBoth of these come with a trade-off. Using regular views and URL confs is more explicit and gives you more control. ViewSets are helpful if you want to get up and running quickly, or when you have a large API and you want to enforce a consistent URL configuration throughout.\n\n\nViewSet actions\n\n\nThe default routers included with REST framework will provide routes for a standard set of create/retrieve/update/destroy style actions, as shown below:\n\n\nclass UserViewSet(viewsets.ViewSet):\n \"\"\"\n Example empty viewset demonstrating the standard\n actions that will be handled by a router class.\n\n If you're using format suffixes, make sure to also include\n the `format=None` keyword argument for each action.\n \"\"\"\n\n def list(self, request):\n pass\n\n def create(self, request):\n pass\n\n def retrieve(self, request, pk=None):\n pass\n\n def update(self, request, pk=None):\n pass\n\n def partial_update(self, request, pk=None):\n pass\n\n def destroy(self, request, pk=None):\n pass\n\n\n\nIntrospecting ViewSet actions\n\n\nDuring dispatch, the following attributes are available on the \nViewSet\n.\n\n\n\n\nbasename\n - the base to use for the URL names that are created.\n\n\naction\n - the name of the current action (e.g., \nlist\n, \ncreate\n).\n\n\ndetail\n - boolean indicating if the current action is configured for a list or detail view.\n\n\nsuffix\n - the display suffix for the viewset type - mirrors the \ndetail\n attribute.\n\n\nname\n - the display name for the viewset. This argument is mutually exclusive to \nsuffix\n.\n\n\ndescription\n - the display description for the individual view of a viewset.\n\n\n\n\nYou may inspect these attributes to adjust behaviour based on the current action. For example, you could restrict permissions to everything except the \nlist\n action similar to this:\n\n\ndef get_permissions(self):\n \"\"\"\n Instantiates and returns the list of permissions that this view requires.\n \"\"\"\n if self.action == 'list':\n permission_classes = [IsAuthenticated]\n else:\n permission_classes = [IsAdmin]\n return [permission() for permission in permission_classes]\n\n\n\nMarking extra actions for routing\n\n\nIf you have ad-hoc methods that should be routable, you can mark them as such with the \n@action\n decorator. Like regular actions, extra actions may be intended for either a single object, or an entire collection. To indicate this, set the \ndetail\n argument to \nTrue\n or \nFalse\n. The router will configure its URL patterns accordingly. e.g., the \nDefaultRouter\n will configure detail actions to contain \npk\n in their URL patterns.\n\n\nA more complete example of extra actions:\n\n\nfrom django.contrib.auth.models import User\nfrom rest_framework import status, viewsets\nfrom rest_framework.decorators import action\nfrom rest_framework.response import Response\nfrom myapp.serializers import UserSerializer, PasswordSerializer\n\nclass UserViewSet(viewsets.ModelViewSet):\n \"\"\"\n A viewset that provides the standard actions\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n @action(detail=True, methods=['post'])\n def set_password(self, request, pk=None):\n user = self.get_object()\n serializer = PasswordSerializer(data=request.data)\n if serializer.is_valid():\n user.set_password(serializer.data['password'])\n user.save()\n return Response({'status': 'password set'})\n else:\n return Response(serializer.errors,\n status=status.HTTP_400_BAD_REQUEST)\n\n @action(detail=False)\n def recent_users(self, request):\n recent_users = User.objects.all().order_by('-last_login')\n\n page = self.paginate_queryset(recent_users)\n if page is not None:\n serializer = self.get_serializer(page, many=True)\n return self.get_paginated_response(serializer.data)\n\n serializer = self.get_serializer(recent_users, many=True)\n return Response(serializer.data)\n\n\n\nThe decorator can additionally take extra arguments that will be set for the routed view only. For example:\n\n\n @action(detail=True, methods=['post'], permission_classes=[IsAdminOrIsSelf])\n def set_password(self, request, pk=None):\n ...\n\n\n\nThe \naction\n decorator will route \nGET\n requests by default, but may also accept other HTTP methods by setting the \nmethods\n argument. For example:\n\n\n @action(detail=True, methods=['post', 'delete'])\n def unset_password(self, request, pk=None):\n ...\n\n\n\nThe two new actions will then be available at the urls \n^users/{pk}/set_password/$\n and \n^users/{pk}/unset_password/$\n\n\nTo view all extra actions, call the \n.get_extra_actions()\n method.\n\n\nRouting additional HTTP methods for extra actions\n\n\nExtra actions can map additional HTTP methods to separate \nViewSet\n methods. For example, the above password set/unset methods could be consolidated into a single route. Note that additional mappings do not accept arguments.\n\n\n @action(detail=True, methods=['put'], name='Change Password')\n def password(self, request, pk=None):\n \"\"\"Update the user's password.\"\"\"\n ...\n\n @password.mapping.delete\n def delete_password(self, request, pk=None):\n \"\"\"Delete the user's password.\"\"\"\n ...\n\n\n\n\nReversing action URLs\n\n\nIf you need to get the URL of an action, use the \n.reverse_action()\n method. This is a convenience wrapper for \nreverse()\n, automatically passing the view's \nrequest\n object and prepending the \nurl_name\n with the \n.basename\n attribute.\n\n\nNote that the \nbasename\n is provided by the router during \nViewSet\n registration. If you are not using a router, then you must provide the \nbasename\n argument to the \n.as_view()\n method.\n\n\nUsing the example from the previous section:\n\n\n>>> view.reverse_action('set-password', args=['1'])\n'http://localhost:8000/api/users/1/set_password'\n\n\n\n\nAlternatively, you can use the \nurl_name\n attribute set by the \n@action\n decorator.\n\n\n>>> view.reverse_action(view.set_password.url_name, args=['1'])\n'http://localhost:8000/api/users/1/set_password'\n\n\n\n\nThe \nurl_name\n argument for \n.reverse_action()\n should match the same argument to the \n@action\n decorator. Additionally, this method can be used to reverse the default actions, such as \nlist\n and \ncreate\n.\n\n\n\n\nAPI Reference\n\n\nViewSet\n\n\nThe \nViewSet\n class inherits from \nAPIView\n. You can use any of the standard attributes such as \npermission_classes\n, \nauthentication_classes\n in order to control the API policy on the viewset.\n\n\nThe \nViewSet\n class does not provide any implementations of actions. In order to use a \nViewSet\n class you'll override the class and define the action implementations explicitly.\n\n\nGenericViewSet\n\n\nThe \nGenericViewSet\n class inherits from \nGenericAPIView\n, and provides the default set of \nget_object\n, \nget_queryset\n methods and other generic view base behavior, but does not include any actions by default.\n\n\nIn order to use a \nGenericViewSet\n class you'll override the class and either mixin the required mixin classes, or define the action implementations explicitly.\n\n\nModelViewSet\n\n\nThe \nModelViewSet\n class inherits from \nGenericAPIView\n and includes implementations for various actions, by mixing in the behavior of the various mixin classes.\n\n\nThe actions provided by the \nModelViewSet\n class are \n.list()\n, \n.retrieve()\n, \n.create()\n, \n.update()\n, \n.partial_update()\n, and \n.destroy()\n.\n\n\nExample\n\n\nBecause \nModelViewSet\n extends \nGenericAPIView\n, you'll normally need to provide at least the \nqueryset\n and \nserializer_class\n attributes. For example:\n\n\nclass AccountViewSet(viewsets.ModelViewSet):\n \"\"\"\n A simple ViewSet for viewing and editing accounts.\n \"\"\"\n queryset = Account.objects.all()\n serializer_class = AccountSerializer\n permission_classes = [IsAccountAdminOrReadOnly]\n\n\n\nNote that you can use any of the standard attributes or method overrides provided by \nGenericAPIView\n. For example, to use a \nViewSet\n that dynamically determines the queryset it should operate on, you might do something like this:\n\n\nclass AccountViewSet(viewsets.ModelViewSet):\n \"\"\"\n A simple ViewSet for viewing and editing the accounts\n associated with the user.\n \"\"\"\n serializer_class = AccountSerializer\n permission_classes = [IsAccountAdminOrReadOnly]\n\n def get_queryset(self):\n return self.request.user.accounts.all()\n\n\n\nNote however that upon removal of the \nqueryset\n property from your \nViewSet\n, any associated \nrouter\n will be unable to derive the basename of your Model automatically, and so you will have to specify the \nbasename\n kwarg as part of your \nrouter registration\n.\n\n\nAlso note that although this class provides the complete set of create/list/retrieve/update/destroy actions by default, you can restrict the available operations by using the standard permission classes.\n\n\nReadOnlyModelViewSet\n\n\nThe \nReadOnlyModelViewSet\n class also inherits from \nGenericAPIView\n. As with \nModelViewSet\n it also includes implementations for various actions, but unlike \nModelViewSet\n only provides the 'read-only' actions, \n.list()\n and \n.retrieve()\n.\n\n\nExample\n\n\nAs with \nModelViewSet\n, you'll normally need to provide at least the \nqueryset\n and \nserializer_class\n attributes. For example:\n\n\nclass AccountViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n A simple ViewSet for viewing accounts.\n \"\"\"\n queryset = Account.objects.all()\n serializer_class = AccountSerializer\n\n\n\nAgain, as with \nModelViewSet\n, you can use any of the standard attributes and method overrides available to \nGenericAPIView\n.\n\n\nCustom ViewSet base classes\n\n\nYou may need to provide custom \nViewSet\n classes that do not have the full set of \nModelViewSet\n actions, or that customize the behavior in some other way.\n\n\nExample\n\n\nTo create a base viewset class that provides \ncreate\n, \nlist\n and \nretrieve\n operations, inherit from \nGenericViewSet\n, and mixin the required actions:\n\n\nfrom rest_framework import mixins\n\nclass CreateListRetrieveViewSet(mixins.CreateModelMixin,\n mixins.ListModelMixin,\n mixins.RetrieveModelMixin,\n viewsets.GenericViewSet):\n \"\"\"\n A viewset that provides `retrieve`, `create`, and `list` actions.\n\n To use it, override the class and set the `.queryset` and\n `.serializer_class` attributes.\n \"\"\"\n pass\n\n\n\nBy creating your own base \nViewSet\n classes, you can provide common behavior that can be reused in multiple viewsets across your API.",
+ "location": "/api-guide/viewsets/",
+ "text": "ViewSets\n\n\n\n\nAfter routing has determined which controller to use for a request, your controller is responsible for making sense of the request and producing the appropriate output.\n\n\n \nRuby on Rails Documentation\n\n\n\n\nDjango REST framework allows you to combine the logic for a set of related views in a single class, called a \nViewSet\n. In other frameworks you may also find conceptually similar implementations named something like 'Resources' or 'Controllers'.\n\n\nA \nViewSet\n class is simply \na type of class-based View, that does not provide any method handlers\n such as \n.get()\n or \n.post()\n, and instead provides actions such as \n.list()\n and \n.create()\n.\n\n\nThe method handlers for a \nViewSet\n are only bound to the corresponding actions at the point of finalizing the view, using the \n.as_view()\n method.\n\n\nTypically, rather than explicitly registering the views in a viewset in the urlconf, you'll register the viewset with a router class, that automatically determines the urlconf for you.\n\n\nExample\n\n\nLet's define a simple viewset that can be used to list or retrieve all the users in the system.\n\n\nfrom django.contrib.auth.models import User\nfrom django.shortcuts import get_object_or_404\nfrom myapps.serializers import UserSerializer\nfrom rest_framework import viewsets\nfrom rest_framework.response import Response\n\nclass UserViewSet(viewsets.ViewSet):\n \"\"\"\n A simple ViewSet for listing or retrieving users.\n \"\"\"\n def list(self, request):\n queryset = User.objects.all()\n serializer = UserSerializer(queryset, many=True)\n return Response(serializer.data)\n\n def retrieve(self, request, pk=None):\n queryset = User.objects.all()\n user = get_object_or_404(queryset, pk=pk)\n serializer = UserSerializer(user)\n return Response(serializer.data)\n\n\n\nIf we need to, we can bind this viewset into two separate views, like so:\n\n\nuser_list = UserViewSet.as_view({'get': 'list'})\nuser_detail = UserViewSet.as_view({'get': 'retrieve'})\n\n\n\nTypically we wouldn't do this, but would instead register the viewset with a router, and allow the urlconf to be automatically generated.\n\n\nfrom myapp.views import UserViewSet\nfrom rest_framework.routers import DefaultRouter\n\nrouter = DefaultRouter()\nrouter.register(r'users', UserViewSet, basename='user')\nurlpatterns = router.urls\n\n\n\nRather than writing your own viewsets, you'll often want to use the existing base classes that provide a default set of behavior. For example:\n\n\nclass UserViewSet(viewsets.ModelViewSet):\n \"\"\"\n A viewset for viewing and editing user instances.\n \"\"\"\n serializer_class = UserSerializer\n queryset = User.objects.all()\n\n\n\nThere are two main advantages of using a \nViewSet\n class over using a \nView\n class.\n\n\n\n\nRepeated logic can be combined into a single class. In the above example, we only need to specify the \nqueryset\n once, and it'll be used across multiple views.\n\n\nBy using routers, we no longer need to deal with wiring up the URL conf ourselves.\n\n\n\n\nBoth of these come with a trade-off. Using regular views and URL confs is more explicit and gives you more control. ViewSets are helpful if you want to get up and running quickly, or when you have a large API and you want to enforce a consistent URL configuration throughout.\n\n\nViewSet actions\n\n\nThe default routers included with REST framework will provide routes for a standard set of create/retrieve/update/destroy style actions, as shown below:\n\n\nclass UserViewSet(viewsets.ViewSet):\n \"\"\"\n Example empty viewset demonstrating the standard\n actions that will be handled by a router class.\n\n If you're using format suffixes, make sure to also include\n the `format=None` keyword argument for each action.\n \"\"\"\n\n def list(self, request):\n pass\n\n def create(self, request):\n pass\n\n def retrieve(self, request, pk=None):\n pass\n\n def update(self, request, pk=None):\n pass\n\n def partial_update(self, request, pk=None):\n pass\n\n def destroy(self, request, pk=None):\n pass\n\n\n\nIntrospecting ViewSet actions\n\n\nDuring dispatch, the following attributes are available on the \nViewSet\n.\n\n\n\n\nbasename\n - the base to use for the URL names that are created.\n\n\naction\n - the name of the current action (e.g., \nlist\n, \ncreate\n).\n\n\ndetail\n - boolean indicating if the current action is configured for a list or detail view.\n\n\nsuffix\n - the display suffix for the viewset type - mirrors the \ndetail\n attribute.\n\n\nname\n - the display name for the viewset. This argument is mutually exclusive to \nsuffix\n.\n\n\ndescription\n - the display description for the individual view of a viewset.\n\n\n\n\nYou may inspect these attributes to adjust behaviour based on the current action. For example, you could restrict permissions to everything except the \nlist\n action similar to this:\n\n\ndef get_permissions(self):\n \"\"\"\n Instantiates and returns the list of permissions that this view requires.\n \"\"\"\n if self.action == 'list':\n permission_classes = [IsAuthenticated]\n else:\n permission_classes = [IsAdmin]\n return [permission() for permission in permission_classes]\n\n\n\nMarking extra actions for routing\n\n\nIf you have ad-hoc methods that should be routable, you can mark them as such with the \n@action\n decorator. Like regular actions, extra actions may be intended for either a single object, or an entire collection. To indicate this, set the \ndetail\n argument to \nTrue\n or \nFalse\n. The router will configure its URL patterns accordingly. e.g., the \nDefaultRouter\n will configure detail actions to contain \npk\n in their URL patterns.\n\n\nA more complete example of extra actions:\n\n\nfrom django.contrib.auth.models import User\nfrom rest_framework import status, viewsets\nfrom rest_framework.decorators import action\nfrom rest_framework.response import Response\nfrom myapp.serializers import UserSerializer, PasswordSerializer\n\nclass UserViewSet(viewsets.ModelViewSet):\n \"\"\"\n A viewset that provides the standard actions\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n @action(detail=True, methods=['post'])\n def set_password(self, request, pk=None):\n user = self.get_object()\n serializer = PasswordSerializer(data=request.data)\n if serializer.is_valid():\n user.set_password(serializer.data['password'])\n user.save()\n return Response({'status': 'password set'})\n else:\n return Response(serializer.errors,\n status=status.HTTP_400_BAD_REQUEST)\n\n @action(detail=False)\n def recent_users(self, request):\n recent_users = User.objects.all().order_by('-last_login')\n\n page = self.paginate_queryset(recent_users)\n if page is not None:\n serializer = self.get_serializer(page, many=True)\n return self.get_paginated_response(serializer.data)\n\n serializer = self.get_serializer(recent_users, many=True)\n return Response(serializer.data)\n\n\n\nThe decorator can additionally take extra arguments that will be set for the routed view only. For example:\n\n\n @action(detail=True, methods=['post'], permission_classes=[IsAdminOrIsSelf])\n def set_password(self, request, pk=None):\n ...\n\n\n\nThe \naction\n decorator will route \nGET\n requests by default, but may also accept other HTTP methods by setting the \nmethods\n argument. For example:\n\n\n @action(detail=True, methods=['post', 'delete'])\n def unset_password(self, request, pk=None):\n ...\n\n\n\nThe two new actions will then be available at the urls \n^users/{pk}/set_password/$\n and \n^users/{pk}/unset_password/$\n\n\nTo view all extra actions, call the \n.get_extra_actions()\n method.\n\n\nRouting additional HTTP methods for extra actions\n\n\nExtra actions can map additional HTTP methods to separate \nViewSet\n methods. For example, the above password set/unset methods could be consolidated into a single route. Note that additional mappings do not accept arguments.\n\n\n @action(detail=True, methods=['put'], name='Change Password')\n def password(self, request, pk=None):\n \nUpdate the user's password.\n\n ...\n\n @password.mapping.delete\n def delete_password(self, request, pk=None):\n \nDelete the user's password.\n\n ...\n\n\n\n\nReversing action URLs\n\n\nIf you need to get the URL of an action, use the \n.reverse_action()\n method. This is a convenience wrapper for \nreverse()\n, automatically passing the view's \nrequest\n object and prepending the \nurl_name\n with the \n.basename\n attribute.\n\n\nNote that the \nbasename\n is provided by the router during \nViewSet\n registration. If you are not using a router, then you must provide the \nbasename\n argument to the \n.as_view()\n method.\n\n\nUsing the example from the previous section:\n\n\n view.reverse_action('set-password', args=['1'])\n'http://localhost:8000/api/users/1/set_password'\n\n\n\n\nAlternatively, you can use the \nurl_name\n attribute set by the \n@action\n decorator.\n\n\n view.reverse_action(view.set_password.url_name, args=['1'])\n'http://localhost:8000/api/users/1/set_password'\n\n\n\n\nThe \nurl_name\n argument for \n.reverse_action()\n should match the same argument to the \n@action\n decorator. Additionally, this method can be used to reverse the default actions, such as \nlist\n and \ncreate\n.\n\n\n\n\nAPI Reference\n\n\nViewSet\n\n\nThe \nViewSet\n class inherits from \nAPIView\n. You can use any of the standard attributes such as \npermission_classes\n, \nauthentication_classes\n in order to control the API policy on the viewset.\n\n\nThe \nViewSet\n class does not provide any implementations of actions. In order to use a \nViewSet\n class you'll override the class and define the action implementations explicitly.\n\n\nGenericViewSet\n\n\nThe \nGenericViewSet\n class inherits from \nGenericAPIView\n, and provides the default set of \nget_object\n, \nget_queryset\n methods and other generic view base behavior, but does not include any actions by default.\n\n\nIn order to use a \nGenericViewSet\n class you'll override the class and either mixin the required mixin classes, or define the action implementations explicitly.\n\n\nModelViewSet\n\n\nThe \nModelViewSet\n class inherits from \nGenericAPIView\n and includes implementations for various actions, by mixing in the behavior of the various mixin classes.\n\n\nThe actions provided by the \nModelViewSet\n class are \n.list()\n, \n.retrieve()\n, \n.create()\n, \n.update()\n, \n.partial_update()\n, and \n.destroy()\n.\n\n\nExample\n\n\nBecause \nModelViewSet\n extends \nGenericAPIView\n, you'll normally need to provide at least the \nqueryset\n and \nserializer_class\n attributes. For example:\n\n\nclass AccountViewSet(viewsets.ModelViewSet):\n \"\"\"\n A simple ViewSet for viewing and editing accounts.\n \"\"\"\n queryset = Account.objects.all()\n serializer_class = AccountSerializer\n permission_classes = [IsAccountAdminOrReadOnly]\n\n\n\nNote that you can use any of the standard attributes or method overrides provided by \nGenericAPIView\n. For example, to use a \nViewSet\n that dynamically determines the queryset it should operate on, you might do something like this:\n\n\nclass AccountViewSet(viewsets.ModelViewSet):\n \"\"\"\n A simple ViewSet for viewing and editing the accounts\n associated with the user.\n \"\"\"\n serializer_class = AccountSerializer\n permission_classes = [IsAccountAdminOrReadOnly]\n\n def get_queryset(self):\n return self.request.user.accounts.all()\n\n\n\nNote however that upon removal of the \nqueryset\n property from your \nViewSet\n, any associated \nrouter\n will be unable to derive the basename of your Model automatically, and so you will have to specify the \nbasename\n kwarg as part of your \nrouter registration\n.\n\n\nAlso note that although this class provides the complete set of create/list/retrieve/update/destroy actions by default, you can restrict the available operations by using the standard permission classes.\n\n\nReadOnlyModelViewSet\n\n\nThe \nReadOnlyModelViewSet\n class also inherits from \nGenericAPIView\n. As with \nModelViewSet\n it also includes implementations for various actions, but unlike \nModelViewSet\n only provides the 'read-only' actions, \n.list()\n and \n.retrieve()\n.\n\n\nExample\n\n\nAs with \nModelViewSet\n, you'll normally need to provide at least the \nqueryset\n and \nserializer_class\n attributes. For example:\n\n\nclass AccountViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n A simple ViewSet for viewing accounts.\n \"\"\"\n queryset = Account.objects.all()\n serializer_class = AccountSerializer\n\n\n\nAgain, as with \nModelViewSet\n, you can use any of the standard attributes and method overrides available to \nGenericAPIView\n.\n\n\nCustom ViewSet base classes\n\n\nYou may need to provide custom \nViewSet\n classes that do not have the full set of \nModelViewSet\n actions, or that customize the behavior in some other way.\n\n\nExample\n\n\nTo create a base viewset class that provides \ncreate\n, \nlist\n and \nretrieve\n operations, inherit from \nGenericViewSet\n, and mixin the required actions:\n\n\nfrom rest_framework import mixins\n\nclass CreateListRetrieveViewSet(mixins.CreateModelMixin,\n mixins.ListModelMixin,\n mixins.RetrieveModelMixin,\n viewsets.GenericViewSet):\n \"\"\"\n A viewset that provides `retrieve`, `create`, and `list` actions.\n\n To use it, override the class and set the `.queryset` and\n `.serializer_class` attributes.\n \"\"\"\n pass\n\n\n\nBy creating your own base \nViewSet\n classes, you can provide common behavior that can be reused in multiple viewsets across your API.",
"title": "Viewsets"
- },
+ },
{
- "location": "/api-guide/viewsets/#viewsets",
- "text": "After routing has determined which controller to use for a request, your controller is responsible for making sense of the request and producing the appropriate output. \u2014 Ruby on Rails Documentation Django REST framework allows you to combine the logic for a set of related views in a single class, called a ViewSet . In other frameworks you may also find conceptually similar implementations named something like 'Resources' or 'Controllers'. A ViewSet class is simply a type of class-based View, that does not provide any method handlers such as .get() or .post() , and instead provides actions such as .list() and .create() . The method handlers for a ViewSet are only bound to the corresponding actions at the point of finalizing the view, using the .as_view() method. Typically, rather than explicitly registering the views in a viewset in the urlconf, you'll register the viewset with a router class, that automatically determines the urlconf for you.",
+ "location": "/api-guide/viewsets/#viewsets",
+ "text": "After routing has determined which controller to use for a request, your controller is responsible for making sense of the request and producing the appropriate output. Ruby on Rails Documentation Django REST framework allows you to combine the logic for a set of related views in a single class, called a ViewSet . In other frameworks you may also find conceptually similar implementations named something like 'Resources' or 'Controllers'. A ViewSet class is simply a type of class-based View, that does not provide any method handlers such as .get() or .post() , and instead provides actions such as .list() and .create() . The method handlers for a ViewSet are only bound to the corresponding actions at the point of finalizing the view, using the .as_view() method. Typically, rather than explicitly registering the views in a viewset in the urlconf, you'll register the viewset with a router class, that automatically determines the urlconf for you.",
"title": "ViewSets"
- },
+ },
{
- "location": "/api-guide/viewsets/#example",
- "text": "Let's define a simple viewset that can be used to list or retrieve all the users in the system. from django.contrib.auth.models import User\nfrom django.shortcuts import get_object_or_404\nfrom myapps.serializers import UserSerializer\nfrom rest_framework import viewsets\nfrom rest_framework.response import Response\n\nclass UserViewSet(viewsets.ViewSet):\n \"\"\"\n A simple ViewSet for listing or retrieving users.\n \"\"\"\n def list(self, request):\n queryset = User.objects.all()\n serializer = UserSerializer(queryset, many=True)\n return Response(serializer.data)\n\n def retrieve(self, request, pk=None):\n queryset = User.objects.all()\n user = get_object_or_404(queryset, pk=pk)\n serializer = UserSerializer(user)\n return Response(serializer.data) If we need to, we can bind this viewset into two separate views, like so: user_list = UserViewSet.as_view({'get': 'list'})\nuser_detail = UserViewSet.as_view({'get': 'retrieve'}) Typically we wouldn't do this, but would instead register the viewset with a router, and allow the urlconf to be automatically generated. from myapp.views import UserViewSet\nfrom rest_framework.routers import DefaultRouter\n\nrouter = DefaultRouter()\nrouter.register(r'users', UserViewSet, basename='user')\nurlpatterns = router.urls Rather than writing your own viewsets, you'll often want to use the existing base classes that provide a default set of behavior. For example: class UserViewSet(viewsets.ModelViewSet):\n \"\"\"\n A viewset for viewing and editing user instances.\n \"\"\"\n serializer_class = UserSerializer\n queryset = User.objects.all() There are two main advantages of using a ViewSet class over using a View class. Repeated logic can be combined into a single class. In the above example, we only need to specify the queryset once, and it'll be used across multiple views. By using routers, we no longer need to deal with wiring up the URL conf ourselves. Both of these come with a trade-off. Using regular views and URL confs is more explicit and gives you more control. ViewSets are helpful if you want to get up and running quickly, or when you have a large API and you want to enforce a consistent URL configuration throughout.",
+ "location": "/api-guide/viewsets/#example",
+ "text": "Let's define a simple viewset that can be used to list or retrieve all the users in the system. from django.contrib.auth.models import User\nfrom django.shortcuts import get_object_or_404\nfrom myapps.serializers import UserSerializer\nfrom rest_framework import viewsets\nfrom rest_framework.response import Response\n\nclass UserViewSet(viewsets.ViewSet):\n \"\"\"\n A simple ViewSet for listing or retrieving users.\n \"\"\"\n def list(self, request):\n queryset = User.objects.all()\n serializer = UserSerializer(queryset, many=True)\n return Response(serializer.data)\n\n def retrieve(self, request, pk=None):\n queryset = User.objects.all()\n user = get_object_or_404(queryset, pk=pk)\n serializer = UserSerializer(user)\n return Response(serializer.data) If we need to, we can bind this viewset into two separate views, like so: user_list = UserViewSet.as_view({'get': 'list'})\nuser_detail = UserViewSet.as_view({'get': 'retrieve'}) Typically we wouldn't do this, but would instead register the viewset with a router, and allow the urlconf to be automatically generated. from myapp.views import UserViewSet\nfrom rest_framework.routers import DefaultRouter\n\nrouter = DefaultRouter()\nrouter.register(r'users', UserViewSet, basename='user')\nurlpatterns = router.urls Rather than writing your own viewsets, you'll often want to use the existing base classes that provide a default set of behavior. For example: class UserViewSet(viewsets.ModelViewSet):\n \"\"\"\n A viewset for viewing and editing user instances.\n \"\"\"\n serializer_class = UserSerializer\n queryset = User.objects.all() There are two main advantages of using a ViewSet class over using a View class. Repeated logic can be combined into a single class. In the above example, we only need to specify the queryset once, and it'll be used across multiple views. By using routers, we no longer need to deal with wiring up the URL conf ourselves. Both of these come with a trade-off. Using regular views and URL confs is more explicit and gives you more control. ViewSets are helpful if you want to get up and running quickly, or when you have a large API and you want to enforce a consistent URL configuration throughout.",
"title": "Example"
- },
+ },
{
- "location": "/api-guide/viewsets/#viewset-actions",
- "text": "The default routers included with REST framework will provide routes for a standard set of create/retrieve/update/destroy style actions, as shown below: class UserViewSet(viewsets.ViewSet):\n \"\"\"\n Example empty viewset demonstrating the standard\n actions that will be handled by a router class.\n\n If you're using format suffixes, make sure to also include\n the `format=None` keyword argument for each action.\n \"\"\"\n\n def list(self, request):\n pass\n\n def create(self, request):\n pass\n\n def retrieve(self, request, pk=None):\n pass\n\n def update(self, request, pk=None):\n pass\n\n def partial_update(self, request, pk=None):\n pass\n\n def destroy(self, request, pk=None):\n pass",
+ "location": "/api-guide/viewsets/#viewset-actions",
+ "text": "The default routers included with REST framework will provide routes for a standard set of create/retrieve/update/destroy style actions, as shown below: class UserViewSet(viewsets.ViewSet):\n \"\"\"\n Example empty viewset demonstrating the standard\n actions that will be handled by a router class.\n\n If you're using format suffixes, make sure to also include\n the `format=None` keyword argument for each action.\n \"\"\"\n\n def list(self, request):\n pass\n\n def create(self, request):\n pass\n\n def retrieve(self, request, pk=None):\n pass\n\n def update(self, request, pk=None):\n pass\n\n def partial_update(self, request, pk=None):\n pass\n\n def destroy(self, request, pk=None):\n pass",
"title": "ViewSet actions"
- },
+ },
{
- "location": "/api-guide/viewsets/#introspecting-viewset-actions",
- "text": "During dispatch, the following attributes are available on the ViewSet . basename - the base to use for the URL names that are created. action - the name of the current action (e.g., list , create ). detail - boolean indicating if the current action is configured for a list or detail view. suffix - the display suffix for the viewset type - mirrors the detail attribute. name - the display name for the viewset. This argument is mutually exclusive to suffix . description - the display description for the individual view of a viewset. You may inspect these attributes to adjust behaviour based on the current action. For example, you could restrict permissions to everything except the list action similar to this: def get_permissions(self):\n \"\"\"\n Instantiates and returns the list of permissions that this view requires.\n \"\"\"\n if self.action == 'list':\n permission_classes = [IsAuthenticated]\n else:\n permission_classes = [IsAdmin]\n return [permission() for permission in permission_classes]",
+ "location": "/api-guide/viewsets/#introspecting-viewset-actions",
+ "text": "During dispatch, the following attributes are available on the ViewSet . basename - the base to use for the URL names that are created. action - the name of the current action (e.g., list , create ). detail - boolean indicating if the current action is configured for a list or detail view. suffix - the display suffix for the viewset type - mirrors the detail attribute. name - the display name for the viewset. This argument is mutually exclusive to suffix . description - the display description for the individual view of a viewset. You may inspect these attributes to adjust behaviour based on the current action. For example, you could restrict permissions to everything except the list action similar to this: def get_permissions(self):\n \"\"\"\n Instantiates and returns the list of permissions that this view requires.\n \"\"\"\n if self.action == 'list':\n permission_classes = [IsAuthenticated]\n else:\n permission_classes = [IsAdmin]\n return [permission() for permission in permission_classes]",
"title": "Introspecting ViewSet actions"
- },
+ },
{
- "location": "/api-guide/viewsets/#marking-extra-actions-for-routing",
- "text": "If you have ad-hoc methods that should be routable, you can mark them as such with the @action decorator. Like regular actions, extra actions may be intended for either a single object, or an entire collection. To indicate this, set the detail argument to True or False . The router will configure its URL patterns accordingly. e.g., the DefaultRouter will configure detail actions to contain pk in their URL patterns. A more complete example of extra actions: from django.contrib.auth.models import User\nfrom rest_framework import status, viewsets\nfrom rest_framework.decorators import action\nfrom rest_framework.response import Response\nfrom myapp.serializers import UserSerializer, PasswordSerializer\n\nclass UserViewSet(viewsets.ModelViewSet):\n \"\"\"\n A viewset that provides the standard actions\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n @action(detail=True, methods=['post'])\n def set_password(self, request, pk=None):\n user = self.get_object()\n serializer = PasswordSerializer(data=request.data)\n if serializer.is_valid():\n user.set_password(serializer.data['password'])\n user.save()\n return Response({'status': 'password set'})\n else:\n return Response(serializer.errors,\n status=status.HTTP_400_BAD_REQUEST)\n\n @action(detail=False)\n def recent_users(self, request):\n recent_users = User.objects.all().order_by('-last_login')\n\n page = self.paginate_queryset(recent_users)\n if page is not None:\n serializer = self.get_serializer(page, many=True)\n return self.get_paginated_response(serializer.data)\n\n serializer = self.get_serializer(recent_users, many=True)\n return Response(serializer.data) The decorator can additionally take extra arguments that will be set for the routed view only. For example: @action(detail=True, methods=['post'], permission_classes=[IsAdminOrIsSelf])\n def set_password(self, request, pk=None):\n ... The action decorator will route GET requests by default, but may also accept other HTTP methods by setting the methods argument. For example: @action(detail=True, methods=['post', 'delete'])\n def unset_password(self, request, pk=None):\n ... The two new actions will then be available at the urls ^users/{pk}/set_password/$ and ^users/{pk}/unset_password/$ To view all extra actions, call the .get_extra_actions() method.",
+ "location": "/api-guide/viewsets/#marking-extra-actions-for-routing",
+ "text": "If you have ad-hoc methods that should be routable, you can mark them as such with the @action decorator. Like regular actions, extra actions may be intended for either a single object, or an entire collection. To indicate this, set the detail argument to True or False . The router will configure its URL patterns accordingly. e.g., the DefaultRouter will configure detail actions to contain pk in their URL patterns. A more complete example of extra actions: from django.contrib.auth.models import User\nfrom rest_framework import status, viewsets\nfrom rest_framework.decorators import action\nfrom rest_framework.response import Response\nfrom myapp.serializers import UserSerializer, PasswordSerializer\n\nclass UserViewSet(viewsets.ModelViewSet):\n \"\"\"\n A viewset that provides the standard actions\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n @action(detail=True, methods=['post'])\n def set_password(self, request, pk=None):\n user = self.get_object()\n serializer = PasswordSerializer(data=request.data)\n if serializer.is_valid():\n user.set_password(serializer.data['password'])\n user.save()\n return Response({'status': 'password set'})\n else:\n return Response(serializer.errors,\n status=status.HTTP_400_BAD_REQUEST)\n\n @action(detail=False)\n def recent_users(self, request):\n recent_users = User.objects.all().order_by('-last_login')\n\n page = self.paginate_queryset(recent_users)\n if page is not None:\n serializer = self.get_serializer(page, many=True)\n return self.get_paginated_response(serializer.data)\n\n serializer = self.get_serializer(recent_users, many=True)\n return Response(serializer.data) The decorator can additionally take extra arguments that will be set for the routed view only. For example: @action(detail=True, methods=['post'], permission_classes=[IsAdminOrIsSelf])\n def set_password(self, request, pk=None):\n ... The action decorator will route GET requests by default, but may also accept other HTTP methods by setting the methods argument. For example: @action(detail=True, methods=['post', 'delete'])\n def unset_password(self, request, pk=None):\n ... The two new actions will then be available at the urls ^users/{pk}/set_password/$ and ^users/{pk}/unset_password/$ To view all extra actions, call the .get_extra_actions() method.",
"title": "Marking extra actions for routing"
- },
+ },
{
- "location": "/api-guide/viewsets/#routing-additional-http-methods-for-extra-actions",
- "text": "Extra actions can map additional HTTP methods to separate ViewSet methods. For example, the above password set/unset methods could be consolidated into a single route. Note that additional mappings do not accept arguments. @action(detail=True, methods=['put'], name='Change Password')\n def password(self, request, pk=None):\n \"\"\"Update the user's password.\"\"\"\n ...\n\n @password.mapping.delete\n def delete_password(self, request, pk=None):\n \"\"\"Delete the user's password.\"\"\"\n ...",
+ "location": "/api-guide/viewsets/#routing-additional-http-methods-for-extra-actions",
+ "text": "Extra actions can map additional HTTP methods to separate ViewSet methods. For example, the above password set/unset methods could be consolidated into a single route. Note that additional mappings do not accept arguments. @action(detail=True, methods=['put'], name='Change Password')\n def password(self, request, pk=None):\n Update the user's password. \n ...\n\n @password.mapping.delete\n def delete_password(self, request, pk=None):\n Delete the user's password. \n ...",
"title": "Routing additional HTTP methods for extra actions"
- },
+ },
{
- "location": "/api-guide/viewsets/#reversing-action-urls",
- "text": "If you need to get the URL of an action, use the .reverse_action() method. This is a convenience wrapper for reverse() , automatically passing the view's request object and prepending the url_name with the .basename attribute. Note that the basename is provided by the router during ViewSet registration. If you are not using a router, then you must provide the basename argument to the .as_view() method. Using the example from the previous section: >>> view.reverse_action('set-password', args=['1'])\n'http://localhost:8000/api/users/1/set_password' Alternatively, you can use the url_name attribute set by the @action decorator. >>> view.reverse_action(view.set_password.url_name, args=['1'])\n'http://localhost:8000/api/users/1/set_password' The url_name argument for .reverse_action() should match the same argument to the @action decorator. Additionally, this method can be used to reverse the default actions, such as list and create .",
+ "location": "/api-guide/viewsets/#reversing-action-urls",
+ "text": "If you need to get the URL of an action, use the .reverse_action() method. This is a convenience wrapper for reverse() , automatically passing the view's request object and prepending the url_name with the .basename attribute. Note that the basename is provided by the router during ViewSet registration. If you are not using a router, then you must provide the basename argument to the .as_view() method. Using the example from the previous section: view.reverse_action('set-password', args=['1'])\n'http://localhost:8000/api/users/1/set_password' Alternatively, you can use the url_name attribute set by the @action decorator. view.reverse_action(view.set_password.url_name, args=['1'])\n'http://localhost:8000/api/users/1/set_password' The url_name argument for .reverse_action() should match the same argument to the @action decorator. Additionally, this method can be used to reverse the default actions, such as list and create .",
"title": "Reversing action URLs"
- },
+ },
{
- "location": "/api-guide/viewsets/#api-reference",
- "text": "",
+ "location": "/api-guide/viewsets/#api-reference",
+ "text": "",
"title": "API Reference"
- },
+ },
{
- "location": "/api-guide/viewsets/#viewset",
- "text": "The ViewSet class inherits from APIView . You can use any of the standard attributes such as permission_classes , authentication_classes in order to control the API policy on the viewset. The ViewSet class does not provide any implementations of actions. In order to use a ViewSet class you'll override the class and define the action implementations explicitly.",
+ "location": "/api-guide/viewsets/#viewset",
+ "text": "The ViewSet class inherits from APIView . You can use any of the standard attributes such as permission_classes , authentication_classes in order to control the API policy on the viewset. The ViewSet class does not provide any implementations of actions. In order to use a ViewSet class you'll override the class and define the action implementations explicitly.",
"title": "ViewSet"
- },
+ },
{
- "location": "/api-guide/viewsets/#genericviewset",
- "text": "The GenericViewSet class inherits from GenericAPIView , and provides the default set of get_object , get_queryset methods and other generic view base behavior, but does not include any actions by default. In order to use a GenericViewSet class you'll override the class and either mixin the required mixin classes, or define the action implementations explicitly.",
+ "location": "/api-guide/viewsets/#genericviewset",
+ "text": "The GenericViewSet class inherits from GenericAPIView , and provides the default set of get_object , get_queryset methods and other generic view base behavior, but does not include any actions by default. In order to use a GenericViewSet class you'll override the class and either mixin the required mixin classes, or define the action implementations explicitly.",
"title": "GenericViewSet"
- },
+ },
{
- "location": "/api-guide/viewsets/#modelviewset",
- "text": "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() , .partial_update() , and .destroy() .",
+ "location": "/api-guide/viewsets/#modelviewset",
+ "text": "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() , .partial_update() , and .destroy() .",
"title": "ModelViewSet"
- },
+ },
{
- "location": "/api-guide/viewsets/#example_1",
- "text": "Because ModelViewSet extends GenericAPIView , you'll normally need to provide at least the queryset and serializer_class attributes. For example: class AccountViewSet(viewsets.ModelViewSet):\n \"\"\"\n A simple ViewSet for viewing and editing accounts.\n \"\"\"\n queryset = Account.objects.all()\n serializer_class = AccountSerializer\n permission_classes = [IsAccountAdminOrReadOnly] Note that you can use any of the standard attributes or method overrides provided by GenericAPIView . For example, to use a ViewSet that dynamically determines the queryset it should operate on, you might do something like this: class AccountViewSet(viewsets.ModelViewSet):\n \"\"\"\n A simple ViewSet for viewing and editing the accounts\n associated with the user.\n \"\"\"\n serializer_class = AccountSerializer\n permission_classes = [IsAccountAdminOrReadOnly]\n\n def get_queryset(self):\n return self.request.user.accounts.all() Note however that upon removal of the queryset property from your ViewSet , any associated router will be unable to derive the basename of your Model automatically, and so you will have to specify the basename kwarg as part of your router registration . Also note that although this class provides the complete set of create/list/retrieve/update/destroy actions by default, you can restrict the available operations by using the standard permission classes.",
+ "location": "/api-guide/viewsets/#example_1",
+ "text": "Because ModelViewSet extends GenericAPIView , you'll normally need to provide at least the queryset and serializer_class attributes. For example: class AccountViewSet(viewsets.ModelViewSet):\n \"\"\"\n A simple ViewSet for viewing and editing accounts.\n \"\"\"\n queryset = Account.objects.all()\n serializer_class = AccountSerializer\n permission_classes = [IsAccountAdminOrReadOnly] Note that you can use any of the standard attributes or method overrides provided by GenericAPIView . For example, to use a ViewSet that dynamically determines the queryset it should operate on, you might do something like this: class AccountViewSet(viewsets.ModelViewSet):\n \"\"\"\n A simple ViewSet for viewing and editing the accounts\n associated with the user.\n \"\"\"\n serializer_class = AccountSerializer\n permission_classes = [IsAccountAdminOrReadOnly]\n\n def get_queryset(self):\n return self.request.user.accounts.all() Note however that upon removal of the queryset property from your ViewSet , any associated router will be unable to derive the basename of your Model automatically, and so you will have to specify the basename kwarg as part of your router registration . Also note that although this class provides the complete set of create/list/retrieve/update/destroy actions by default, you can restrict the available operations by using the standard permission classes.",
"title": "Example"
- },
+ },
{
- "location": "/api-guide/viewsets/#readonlymodelviewset",
- "text": "The ReadOnlyModelViewSet class also inherits from GenericAPIView . As with ModelViewSet it also includes implementations for various actions, but unlike ModelViewSet only provides the 'read-only' actions, .list() and .retrieve() .",
+ "location": "/api-guide/viewsets/#readonlymodelviewset",
+ "text": "The ReadOnlyModelViewSet class also inherits from GenericAPIView . As with ModelViewSet it also includes implementations for various actions, but unlike ModelViewSet only provides the 'read-only' actions, .list() and .retrieve() .",
"title": "ReadOnlyModelViewSet"
- },
+ },
{
- "location": "/api-guide/viewsets/#example_2",
- "text": "As with ModelViewSet , you'll normally need to provide at least the queryset and serializer_class attributes. For example: class AccountViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n A simple ViewSet for viewing accounts.\n \"\"\"\n queryset = Account.objects.all()\n serializer_class = AccountSerializer Again, as with ModelViewSet , you can use any of the standard attributes and method overrides available to GenericAPIView .",
+ "location": "/api-guide/viewsets/#example_2",
+ "text": "As with ModelViewSet , you'll normally need to provide at least the queryset and serializer_class attributes. For example: class AccountViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n A simple ViewSet for viewing accounts.\n \"\"\"\n queryset = Account.objects.all()\n serializer_class = AccountSerializer Again, as with ModelViewSet , you can use any of the standard attributes and method overrides available to GenericAPIView .",
"title": "Example"
- },
+ },
{
- "location": "/api-guide/viewsets/#custom-viewset-base-classes",
- "text": "You may need to provide custom ViewSet classes that do not have the full set of ModelViewSet actions, or that customize the behavior in some other way.",
+ "location": "/api-guide/viewsets/#custom-viewset-base-classes",
+ "text": "You may need to provide custom ViewSet classes that do not have the full set of ModelViewSet actions, or that customize the behavior in some other way.",
"title": "Custom ViewSet base classes"
- },
+ },
{
- "location": "/api-guide/viewsets/#example_3",
- "text": "To create a base viewset class that provides create , list and retrieve operations, inherit from GenericViewSet , and mixin the required actions: from rest_framework import mixins\n\nclass CreateListRetrieveViewSet(mixins.CreateModelMixin,\n mixins.ListModelMixin,\n mixins.RetrieveModelMixin,\n viewsets.GenericViewSet):\n \"\"\"\n A viewset that provides `retrieve`, `create`, and `list` actions.\n\n To use it, override the class and set the `.queryset` and\n `.serializer_class` attributes.\n \"\"\"\n pass By creating your own base ViewSet classes, you can provide common behavior that can be reused in multiple viewsets across your API.",
+ "location": "/api-guide/viewsets/#example_3",
+ "text": "To create a base viewset class that provides create , list and retrieve operations, inherit from GenericViewSet , and mixin the required actions: from rest_framework import mixins\n\nclass CreateListRetrieveViewSet(mixins.CreateModelMixin,\n mixins.ListModelMixin,\n mixins.RetrieveModelMixin,\n viewsets.GenericViewSet):\n \"\"\"\n A viewset that provides `retrieve`, `create`, and `list` actions.\n\n To use it, override the class and set the `.queryset` and\n `.serializer_class` attributes.\n \"\"\"\n pass By creating your own base ViewSet classes, you can provide common behavior that can be reused in multiple viewsets across your API.",
"title": "Example"
- },
+ },
{
- "location": "/api-guide/routers/",
- "text": "Routers\n\n\n\n\nResource routing allows you to quickly declare all of the common routes for a given resourceful controller. Instead of declaring separate routes for your index... a resourceful route declares them in a single line of code.\n\n\n\u2014 \nRuby on Rails Documentation\n\n\n\n\nSome Web frameworks such as Rails provide functionality for automatically determining how the URLs for an application should be mapped to the logic that deals with handling incoming requests.\n\n\nREST framework adds support for automatic URL routing to Django, and provides you with a simple, quick and consistent way of wiring your view logic to a set of URLs.\n\n\nUsage\n\n\nHere's an example of a simple URL conf, that uses \nSimpleRouter\n.\n\n\nfrom rest_framework import routers\n\nrouter = routers.SimpleRouter()\nrouter.register(r'users', UserViewSet)\nrouter.register(r'accounts', AccountViewSet)\nurlpatterns = router.urls\n\n\n\nThere are two mandatory arguments to the \nregister()\n method:\n\n\n\n\nprefix\n - The URL prefix to use for this set of routes.\n\n\nviewset\n - The viewset class.\n\n\n\n\nOptionally, you may also specify an additional argument:\n\n\n\n\nbasename\n - The base to use for the URL names that are created. If unset the basename will be automatically generated based on the \nqueryset\n attribute of the viewset, if it has one. Note that if the viewset does not include a \nqueryset\n attribute then you must set \nbasename\n when registering the viewset.\n\n\n\n\nThe example above would generate the following URL patterns:\n\n\n\n\nURL pattern: \n^users/$\n Name: \n'user-list'\n\n\nURL pattern: \n^users/{pk}/$\n Name: \n'user-detail'\n\n\nURL pattern: \n^accounts/$\n Name: \n'account-list'\n\n\nURL pattern: \n^accounts/{pk}/$\n Name: \n'account-detail'\n\n\n\n\n\n\nNote\n: The \nbasename\n argument is used to specify the initial part of the view name pattern. In the example above, that's the \nuser\n or \naccount\n part.\n\n\nTypically you won't \nneed\n to specify the \nbasename\n argument, but if you have a viewset where you've defined a custom \nget_queryset\n method, then the viewset may not have a \n.queryset\n attribute set. If you try to register that viewset you'll see an error like this:\n\n\n'basename' argument not specified, and could not automatically determine the name from the viewset, as it does not have a '.queryset' attribute.\n\n\n\nThis means you'll need to explicitly set the \nbasename\n argument when registering the viewset, as it could not be automatically determined from the model name.\n\n\n\n\nUsing \ninclude\n with routers\n\n\nThe \n.urls\n attribute on a router instance is simply a standard list of URL patterns. There are a number of different styles for how you can include these URLs.\n\n\nFor example, you can append \nrouter.urls\n to a list of existing views...\n\n\nrouter = routers.SimpleRouter()\nrouter.register(r'users', UserViewSet)\nrouter.register(r'accounts', AccountViewSet)\n\nurlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n]\n\nurlpatterns += router.urls\n\n\n\nAlternatively you can use Django's \ninclude\n function, like so...\n\n\nurlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^', include(router.urls)),\n]\n\n\n\nYou may use \ninclude\n with an application namespace:\n\n\nurlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^api/', include((router.urls, 'app_name'))),\n]\n\n\n\nOr both an application and instance namespace:\n\n\nurlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^api/', include((router.urls, 'app_name'), namespace='instance_name')),\n]\n\n\n\nSee Django's \nURL namespaces docs\n and the \ninclude\n API reference\n for more details.\n\n\n\n\nNote\n: If using namespacing with hyperlinked serializers you'll also need to ensure that any \nview_name\n parameters\non the serializers correctly reflect the namespace. In the examples above you'd need to include a parameter such as\n\nview_name='app_name:user-detail'\n for serializer fields hyperlinked to the user detail view.\n\n\nThe automatic \nview_name\n generation uses a pattern like \n%(model_name)-detail\n. Unless your models names actually clash\nyou may be better off \nnot\n namespacing your Django REST Framework views when using hyperlinked serializers.\n\n\n\n\nRouting for extra actions\n\n\nA viewset may \nmark extra actions for routing\n by decorating a method with the \n@action\n decorator. These extra actions will be included in the generated routes. For example, given the \nset_password\n method on the \nUserViewSet\n class:\n\n\nfrom myapp.permissions import IsAdminOrIsSelf\nfrom rest_framework.decorators import action\n\nclass UserViewSet(ModelViewSet):\n ...\n\n @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf])\n def set_password(self, request, pk=None):\n ...\n\n\n\nThe following route would be generated:\n\n\n\n\nURL pattern: \n^users/{pk}/set_password/$\n\n\nURL name: \n'user-set-password'\n\n\n\n\nBy default, the URL pattern is based on the method name, and the URL name is the combination of the \nViewSet.basename\n and the hyphenated method name.\nIf you don't want to use the defaults for either of these values, you can instead provide the \nurl_path\n and \nurl_name\n arguments to the \n@action\n decorator.\n\n\nFor example, if you want to change the URL for our custom action to \n^users/{pk}/change-password/$\n, you could write:\n\n\nfrom myapp.permissions import IsAdminOrIsSelf\nfrom rest_framework.decorators import action\n\nclass UserViewSet(ModelViewSet):\n ...\n\n @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf],\n url_path='change-password', url_name='change_password')\n def set_password(self, request, pk=None):\n ...\n\n\n\nThe above example would now generate the following URL pattern:\n\n\n\n\nURL path: \n^users/{pk}/change-password/$\n\n\nURL name: \n'user-change_password'\n\n\n\n\nAPI Guide\n\n\nSimpleRouter\n\n\nThis router includes routes for the standard set of \nlist\n, \ncreate\n, \nretrieve\n, \nupdate\n, \npartial_update\n and \ndestroy\n actions. The viewset can also mark additional methods to be routed, using the \n@action\n decorator.\n\n\n\n \nURL Style\nHTTP Method\nAction\nURL Name\n\n \n{prefix}/\nGET\nlist\n{basename}-list\n\n \nPOST\ncreate\n\n \n{prefix}/{url_path}/\nGET, or as specified by `methods` argument\n`@action(detail=False)` decorated method\n{basename}-{url_name}\n\n \n{prefix}/{lookup}/\nGET\nretrieve\n{basename}-detail\n\n \nPUT\nupdate\n\n \nPATCH\npartial_update\n\n \nDELETE\ndestroy\n\n \n{prefix}/{lookup}/{url_path}/\nGET, or as specified by `methods` argument\n`@action(detail=True)` decorated method\n{basename}-{url_name}\n\n\n\n\n\nBy default the URLs created by \nSimpleRouter\n are appended with a trailing slash.\nThis behavior can be modified by setting the \ntrailing_slash\n argument to \nFalse\n when instantiating the router. For example:\n\n\nrouter = SimpleRouter(trailing_slash=False)\n\n\n\nTrailing slashes are conventional in Django, but are not used by default in some other frameworks such as Rails. Which style you choose to use is largely a matter of preference, although some javascript frameworks may expect a particular routing style.\n\n\nThe router will match lookup values containing any characters except slashes and period characters. For a more restrictive (or lenient) lookup pattern, set the \nlookup_value_regex\n attribute on the viewset. For example, you can limit the lookup to valid UUIDs:\n\n\nclass MyModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):\n lookup_field = 'my_model_id'\n lookup_value_regex = '[0-9a-f]{32}'\n\n\n\nDefaultRouter\n\n\nThis router is similar to \nSimpleRouter\n as above, but additionally includes a default API root view, that returns a response containing hyperlinks to all the list views. It also generates routes for optional \n.json\n style format suffixes.\n\n\n\n \nURL Style\nHTTP Method\nAction\nURL Name\n\n \n[.format]\nGET\nautomatically generated root view\napi-root\n\n \n{prefix}/[.format]\nGET\nlist\n{basename}-list\n\n \nPOST\ncreate\n\n \n{prefix}/{url_path}/[.format]\nGET, or as specified by `methods` argument\n`@action(detail=False)` decorated method\n{basename}-{url_name}\n\n \n{prefix}/{lookup}/[.format]\nGET\nretrieve\n{basename}-detail\n\n \nPUT\nupdate\n\n \nPATCH\npartial_update\n\n \nDELETE\ndestroy\n\n \n{prefix}/{lookup}/{url_path}/[.format]\nGET, or as specified by `methods` argument\n`@action(detail=True)` decorated method\n{basename}-{url_name}\n\n\n\n\n\nAs with \nSimpleRouter\n the trailing slashes on the URL routes can be removed by setting the \ntrailing_slash\n argument to \nFalse\n when instantiating the router.\n\n\nrouter = DefaultRouter(trailing_slash=False)\n\n\n\nCustom Routers\n\n\nImplementing a custom router isn't something you'd need to do very often, but it can be useful if you have specific requirements about how the URLs for your API are structured. Doing so allows you to encapsulate the URL structure in a reusable way that ensures you don't have to write your URL patterns explicitly for each new view.\n\n\nThe simplest way to implement a custom router is to subclass one of the existing router classes. The \n.routes\n attribute is used to template the URL patterns that will be mapped to each viewset. The \n.routes\n attribute is a list of \nRoute\n named tuples.\n\n\nThe arguments to the \nRoute\n named tuple are:\n\n\nurl\n: A string representing the URL to be routed. May include the following format strings:\n\n\n\n\n{prefix}\n - The URL prefix to use for this set of routes.\n\n\n{lookup}\n - The lookup field used to match against a single instance.\n\n\n{trailing_slash}\n - Either a '/' or an empty string, depending on the \ntrailing_slash\n argument.\n\n\n\n\nmapping\n: A mapping of HTTP method names to the view methods\n\n\nname\n: The name of the URL as used in \nreverse\n calls. May include the following format string:\n\n\n\n\n{basename}\n - The base to use for the URL names that are created.\n\n\n\n\ninitkwargs\n: A dictionary of any additional arguments that should be passed when instantiating the view. Note that the \ndetail\n, \nbasename\n, and \nsuffix\n arguments are reserved for viewset introspection and are also used by the browsable API to generate the view name and breadcrumb links.\n\n\nCustomizing dynamic routes\n\n\nYou can also customize how the \n@action\n decorator is routed. Include the \nDynamicRoute\n named tuple in the \n.routes\n list, setting the \ndetail\n argument as appropriate for the list-based and detail-based routes. In addition to \ndetail\n, the arguments to \nDynamicRoute\n are:\n\n\nurl\n: A string representing the URL to be routed. May include the same format strings as \nRoute\n, and additionally accepts the \n{url_path}\n format string.\n\n\nname\n: The name of the URL as used in \nreverse\n calls. May include the following format strings:\n\n\n\n\n{basename}\n - The base to use for the URL names that are created.\n\n\n{url_name}\n - The \nurl_name\n provided to the \n@action\n.\n\n\n\n\ninitkwargs\n: A dictionary of any additional arguments that should be passed when instantiating the view.\n\n\nExample\n\n\nThe following example will only route to the \nlist\n and \nretrieve\n actions, and does not use the trailing slash convention.\n\n\nfrom rest_framework.routers import Route, DynamicRoute, SimpleRouter\n\nclass CustomReadOnlyRouter(SimpleRouter):\n \"\"\"\n A router for read-only APIs, which doesn't use trailing slashes.\n \"\"\"\n routes = [\n Route(\n url=r'^{prefix}$',\n mapping={'get': 'list'},\n name='{basename}-list',\n detail=False,\n initkwargs={'suffix': 'List'}\n ),\n Route(\n url=r'^{prefix}/{lookup}$',\n mapping={'get': 'retrieve'},\n name='{basename}-detail',\n detail=True,\n initkwargs={'suffix': 'Detail'}\n ),\n DynamicRoute(\n url=r'^{prefix}/{lookup}/{url_path}$',\n name='{basename}-{url_name}',\n detail=True,\n initkwargs={}\n )\n ]\n\n\n\nLet's take a look at the routes our \nCustomReadOnlyRouter\n would generate for a simple viewset.\n\n\nviews.py\n:\n\n\nclass UserViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n A viewset that provides the standard actions\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer\n lookup_field = 'username'\n\n @action(detail=True)\n def group_names(self, request, pk=None):\n \"\"\"\n Returns a list of all the group names that the given\n user belongs to.\n \"\"\"\n user = self.get_object()\n groups = user.groups.all()\n return Response([group.name for group in groups])\n\n\n\nurls.py\n:\n\n\nrouter = CustomReadOnlyRouter()\nrouter.register('users', UserViewSet)\nurlpatterns = router.urls\n\n\n\nThe following mappings would be generated...\n\n\n\n \nURL\nHTTP Method\nAction\nURL Name\n\n \n/users\nGET\nlist\nuser-list\n\n \n/users/{username}\nGET\nretrieve\nuser-detail\n\n \n/users/{username}/group_names\nGET\ngroup_names\nuser-group-names\n\n\n\n\n\nFor another example of setting the \n.routes\n attribute, see the source code for the \nSimpleRouter\n class.\n\n\nAdvanced custom routers\n\n\nIf you want to provide totally custom behavior, you can override \nBaseRouter\n and override the \nget_urls(self)\n method. The method should inspect the registered viewsets and return a list of URL patterns. The registered prefix, viewset and basename tuples may be inspected by accessing the \nself.registry\n attribute.\n\n\nYou may also want to override the \nget_default_basename(self, viewset)\n method, or else always explicitly set the \nbasename\n argument when registering your viewsets with the router.\n\n\nThird Party Packages\n\n\nThe following third party packages are also available.\n\n\nDRF Nested Routers\n\n\nThe \ndrf-nested-routers package\n provides routers and relationship fields for working with nested resources.\n\n\nModelRouter (wq.db.rest)\n\n\nThe \nwq.db package\n provides an advanced \nModelRouter\n class (and singleton instance) that extends \nDefaultRouter\n with a \nregister_model()\n API. Much like Django's \nadmin.site.register\n, the only required argument to \nrest.router.register_model\n is a model class. Reasonable defaults for a url prefix, serializer, and viewset will be inferred from the model and global configuration.\n\n\nfrom wq.db import rest\nfrom myapp.models import MyModel\n\nrest.router.register_model(MyModel)\n\n\n\nDRF-extensions\n\n\nThe \nDRF-extensions\n package\n provides \nrouters\n for creating \nnested viewsets\n, \ncollection level controllers\n with \ncustomizable endpoint names\n.",
+ "location": "/api-guide/routers/",
+ "text": "Routers\n\n\n\n\nResource routing allows you to quickly declare all of the common routes for a given resourceful controller. Instead of declaring separate routes for your index... a resourceful route declares them in a single line of code.\n\n\n \nRuby on Rails Documentation\n\n\n\n\nSome Web frameworks such as Rails provide functionality for automatically determining how the URLs for an application should be mapped to the logic that deals with handling incoming requests.\n\n\nREST framework adds support for automatic URL routing to Django, and provides you with a simple, quick and consistent way of wiring your view logic to a set of URLs.\n\n\nUsage\n\n\nHere's an example of a simple URL conf, that uses \nSimpleRouter\n.\n\n\nfrom rest_framework import routers\n\nrouter = routers.SimpleRouter()\nrouter.register(r'users', UserViewSet)\nrouter.register(r'accounts', AccountViewSet)\nurlpatterns = router.urls\n\n\n\nThere are two mandatory arguments to the \nregister()\n method:\n\n\n\n\nprefix\n - The URL prefix to use for this set of routes.\n\n\nviewset\n - The viewset class.\n\n\n\n\nOptionally, you may also specify an additional argument:\n\n\n\n\nbasename\n - The base to use for the URL names that are created. If unset the basename will be automatically generated based on the \nqueryset\n attribute of the viewset, if it has one. Note that if the viewset does not include a \nqueryset\n attribute then you must set \nbasename\n when registering the viewset.\n\n\n\n\nThe example above would generate the following URL patterns:\n\n\n\n\nURL pattern: \n^users/$\n Name: \n'user-list'\n\n\nURL pattern: \n^users/{pk}/$\n Name: \n'user-detail'\n\n\nURL pattern: \n^accounts/$\n Name: \n'account-list'\n\n\nURL pattern: \n^accounts/{pk}/$\n Name: \n'account-detail'\n\n\n\n\n\n\nNote\n: The \nbasename\n argument is used to specify the initial part of the view name pattern. In the example above, that's the \nuser\n or \naccount\n part.\n\n\nTypically you won't \nneed\n to specify the \nbasename\n argument, but if you have a viewset where you've defined a custom \nget_queryset\n method, then the viewset may not have a \n.queryset\n attribute set. If you try to register that viewset you'll see an error like this:\n\n\n'basename' argument not specified, and could not automatically determine the name from the viewset, as it does not have a '.queryset' attribute.\n\n\n\nThis means you'll need to explicitly set the \nbasename\n argument when registering the viewset, as it could not be automatically determined from the model name.\n\n\n\n\nUsing \ninclude\n with routers\n\n\nThe \n.urls\n attribute on a router instance is simply a standard list of URL patterns. There are a number of different styles for how you can include these URLs.\n\n\nFor example, you can append \nrouter.urls\n to a list of existing views...\n\n\nrouter = routers.SimpleRouter()\nrouter.register(r'users', UserViewSet)\nrouter.register(r'accounts', AccountViewSet)\n\nurlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n]\n\nurlpatterns += router.urls\n\n\n\nAlternatively you can use Django's \ninclude\n function, like so...\n\n\nurlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^', include(router.urls)),\n]\n\n\n\nYou may use \ninclude\n with an application namespace:\n\n\nurlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^api/', include((router.urls, 'app_name'))),\n]\n\n\n\nOr both an application and instance namespace:\n\n\nurlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^api/', include((router.urls, 'app_name'), namespace='instance_name')),\n]\n\n\n\nSee Django's \nURL namespaces docs\n and the \ninclude\n API reference\n for more details.\n\n\n\n\nNote\n: If using namespacing with hyperlinked serializers you'll also need to ensure that any \nview_name\n parameters\non the serializers correctly reflect the namespace. In the examples above you'd need to include a parameter such as\n\nview_name='app_name:user-detail'\n for serializer fields hyperlinked to the user detail view.\n\n\nThe automatic \nview_name\n generation uses a pattern like \n%(model_name)-detail\n. Unless your models names actually clash\nyou may be better off \nnot\n namespacing your Django REST Framework views when using hyperlinked serializers.\n\n\n\n\nRouting for extra actions\n\n\nA viewset may \nmark extra actions for routing\n by decorating a method with the \n@action\n decorator. These extra actions will be included in the generated routes. For example, given the \nset_password\n method on the \nUserViewSet\n class:\n\n\nfrom myapp.permissions import IsAdminOrIsSelf\nfrom rest_framework.decorators import action\n\nclass UserViewSet(ModelViewSet):\n ...\n\n @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf])\n def set_password(self, request, pk=None):\n ...\n\n\n\nThe following route would be generated:\n\n\n\n\nURL pattern: \n^users/{pk}/set_password/$\n\n\nURL name: \n'user-set-password'\n\n\n\n\nBy default, the URL pattern is based on the method name, and the URL name is the combination of the \nViewSet.basename\n and the hyphenated method name.\nIf you don't want to use the defaults for either of these values, you can instead provide the \nurl_path\n and \nurl_name\n arguments to the \n@action\n decorator.\n\n\nFor example, if you want to change the URL for our custom action to \n^users/{pk}/change-password/$\n, you could write:\n\n\nfrom myapp.permissions import IsAdminOrIsSelf\nfrom rest_framework.decorators import action\n\nclass UserViewSet(ModelViewSet):\n ...\n\n @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf],\n url_path='change-password', url_name='change_password')\n def set_password(self, request, pk=None):\n ...\n\n\n\nThe above example would now generate the following URL pattern:\n\n\n\n\nURL path: \n^users/{pk}/change-password/$\n\n\nURL name: \n'user-change_password'\n\n\n\n\nAPI Guide\n\n\nSimpleRouter\n\n\nThis router includes routes for the standard set of \nlist\n, \ncreate\n, \nretrieve\n, \nupdate\n, \npartial_update\n and \ndestroy\n actions. The viewset can also mark additional methods to be routed, using the \n@action\n decorator.\n\n\n\n \nURL Style\nHTTP Method\nAction\nURL Name\n\n \n{prefix}/\nGET\nlist\n{basename}-list\n\n \nPOST\ncreate\n\n \n{prefix}/{url_path}/\nGET, or as specified by `methods` argument\n`@action(detail=False)` decorated method\n{basename}-{url_name}\n\n \n{prefix}/{lookup}/\nGET\nretrieve\n{basename}-detail\n\n \nPUT\nupdate\n\n \nPATCH\npartial_update\n\n \nDELETE\ndestroy\n\n \n{prefix}/{lookup}/{url_path}/\nGET, or as specified by `methods` argument\n`@action(detail=True)` decorated method\n{basename}-{url_name}\n\n\n\n\n\nBy default the URLs created by \nSimpleRouter\n are appended with a trailing slash.\nThis behavior can be modified by setting the \ntrailing_slash\n argument to \nFalse\n when instantiating the router. For example:\n\n\nrouter = SimpleRouter(trailing_slash=False)\n\n\n\nTrailing slashes are conventional in Django, but are not used by default in some other frameworks such as Rails. Which style you choose to use is largely a matter of preference, although some javascript frameworks may expect a particular routing style.\n\n\nThe router will match lookup values containing any characters except slashes and period characters. For a more restrictive (or lenient) lookup pattern, set the \nlookup_value_regex\n attribute on the viewset. For example, you can limit the lookup to valid UUIDs:\n\n\nclass MyModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):\n lookup_field = 'my_model_id'\n lookup_value_regex = '[0-9a-f]{32}'\n\n\n\nDefaultRouter\n\n\nThis router is similar to \nSimpleRouter\n as above, but additionally includes a default API root view, that returns a response containing hyperlinks to all the list views. It also generates routes for optional \n.json\n style format suffixes.\n\n\n\n \nURL Style\nHTTP Method\nAction\nURL Name\n\n \n[.format]\nGET\nautomatically generated root view\napi-root\n\n \n{prefix}/[.format]\nGET\nlist\n{basename}-list\n\n \nPOST\ncreate\n\n \n{prefix}/{url_path}/[.format]\nGET, or as specified by `methods` argument\n`@action(detail=False)` decorated method\n{basename}-{url_name}\n\n \n{prefix}/{lookup}/[.format]\nGET\nretrieve\n{basename}-detail\n\n \nPUT\nupdate\n\n \nPATCH\npartial_update\n\n \nDELETE\ndestroy\n\n \n{prefix}/{lookup}/{url_path}/[.format]\nGET, or as specified by `methods` argument\n`@action(detail=True)` decorated method\n{basename}-{url_name}\n\n\n\n\n\nAs with \nSimpleRouter\n the trailing slashes on the URL routes can be removed by setting the \ntrailing_slash\n argument to \nFalse\n when instantiating the router.\n\n\nrouter = DefaultRouter(trailing_slash=False)\n\n\n\nCustom Routers\n\n\nImplementing a custom router isn't something you'd need to do very often, but it can be useful if you have specific requirements about how the URLs for your API are structured. Doing so allows you to encapsulate the URL structure in a reusable way that ensures you don't have to write your URL patterns explicitly for each new view.\n\n\nThe simplest way to implement a custom router is to subclass one of the existing router classes. The \n.routes\n attribute is used to template the URL patterns that will be mapped to each viewset. The \n.routes\n attribute is a list of \nRoute\n named tuples.\n\n\nThe arguments to the \nRoute\n named tuple are:\n\n\nurl\n: A string representing the URL to be routed. May include the following format strings:\n\n\n\n\n{prefix}\n - The URL prefix to use for this set of routes.\n\n\n{lookup}\n - The lookup field used to match against a single instance.\n\n\n{trailing_slash}\n - Either a '/' or an empty string, depending on the \ntrailing_slash\n argument.\n\n\n\n\nmapping\n: A mapping of HTTP method names to the view methods\n\n\nname\n: The name of the URL as used in \nreverse\n calls. May include the following format string:\n\n\n\n\n{basename}\n - The base to use for the URL names that are created.\n\n\n\n\ninitkwargs\n: A dictionary of any additional arguments that should be passed when instantiating the view. Note that the \ndetail\n, \nbasename\n, and \nsuffix\n arguments are reserved for viewset introspection and are also used by the browsable API to generate the view name and breadcrumb links.\n\n\nCustomizing dynamic routes\n\n\nYou can also customize how the \n@action\n decorator is routed. Include the \nDynamicRoute\n named tuple in the \n.routes\n list, setting the \ndetail\n argument as appropriate for the list-based and detail-based routes. In addition to \ndetail\n, the arguments to \nDynamicRoute\n are:\n\n\nurl\n: A string representing the URL to be routed. May include the same format strings as \nRoute\n, and additionally accepts the \n{url_path}\n format string.\n\n\nname\n: The name of the URL as used in \nreverse\n calls. May include the following format strings:\n\n\n\n\n{basename}\n - The base to use for the URL names that are created.\n\n\n{url_name}\n - The \nurl_name\n provided to the \n@action\n.\n\n\n\n\ninitkwargs\n: A dictionary of any additional arguments that should be passed when instantiating the view.\n\n\nExample\n\n\nThe following example will only route to the \nlist\n and \nretrieve\n actions, and does not use the trailing slash convention.\n\n\nfrom rest_framework.routers import Route, DynamicRoute, SimpleRouter\n\nclass CustomReadOnlyRouter(SimpleRouter):\n \"\"\"\n A router for read-only APIs, which doesn't use trailing slashes.\n \"\"\"\n routes = [\n Route(\n url=r'^{prefix}$',\n mapping={'get': 'list'},\n name='{basename}-list',\n detail=False,\n initkwargs={'suffix': 'List'}\n ),\n Route(\n url=r'^{prefix}/{lookup}$',\n mapping={'get': 'retrieve'},\n name='{basename}-detail',\n detail=True,\n initkwargs={'suffix': 'Detail'}\n ),\n DynamicRoute(\n url=r'^{prefix}/{lookup}/{url_path}$',\n name='{basename}-{url_name}',\n detail=True,\n initkwargs={}\n )\n ]\n\n\n\nLet's take a look at the routes our \nCustomReadOnlyRouter\n would generate for a simple viewset.\n\n\nviews.py\n:\n\n\nclass UserViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n A viewset that provides the standard actions\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer\n lookup_field = 'username'\n\n @action(detail=True)\n def group_names(self, request, pk=None):\n \"\"\"\n Returns a list of all the group names that the given\n user belongs to.\n \"\"\"\n user = self.get_object()\n groups = user.groups.all()\n return Response([group.name for group in groups])\n\n\n\nurls.py\n:\n\n\nrouter = CustomReadOnlyRouter()\nrouter.register('users', UserViewSet)\nurlpatterns = router.urls\n\n\n\nThe following mappings would be generated...\n\n\n\n \nURL\nHTTP Method\nAction\nURL Name\n\n \n/users\nGET\nlist\nuser-list\n\n \n/users/{username}\nGET\nretrieve\nuser-detail\n\n \n/users/{username}/group_names\nGET\ngroup_names\nuser-group-names\n\n\n\n\n\nFor another example of setting the \n.routes\n attribute, see the source code for the \nSimpleRouter\n class.\n\n\nAdvanced custom routers\n\n\nIf you want to provide totally custom behavior, you can override \nBaseRouter\n and override the \nget_urls(self)\n method. The method should inspect the registered viewsets and return a list of URL patterns. The registered prefix, viewset and basename tuples may be inspected by accessing the \nself.registry\n attribute.\n\n\nYou may also want to override the \nget_default_basename(self, viewset)\n method, or else always explicitly set the \nbasename\n argument when registering your viewsets with the router.\n\n\nThird Party Packages\n\n\nThe following third party packages are also available.\n\n\nDRF Nested Routers\n\n\nThe \ndrf-nested-routers package\n provides routers and relationship fields for working with nested resources.\n\n\nModelRouter (wq.db.rest)\n\n\nThe \nwq.db package\n provides an advanced \nModelRouter\n class (and singleton instance) that extends \nDefaultRouter\n with a \nregister_model()\n API. Much like Django's \nadmin.site.register\n, the only required argument to \nrest.router.register_model\n is a model class. Reasonable defaults for a url prefix, serializer, and viewset will be inferred from the model and global configuration.\n\n\nfrom wq.db import rest\nfrom myapp.models import MyModel\n\nrest.router.register_model(MyModel)\n\n\n\nDRF-extensions\n\n\nThe \nDRF-extensions\n package\n provides \nrouters\n for creating \nnested viewsets\n, \ncollection level controllers\n with \ncustomizable endpoint names\n.",
"title": "Routers"
- },
+ },
{
- "location": "/api-guide/routers/#routers",
- "text": "Resource routing allows you to quickly declare all of the common routes for a given resourceful controller. Instead of declaring separate routes for your index... a resourceful route declares them in a single line of code. \u2014 Ruby on Rails Documentation Some Web frameworks such as Rails provide functionality for automatically determining how the URLs for an application should be mapped to the logic that deals with handling incoming requests. REST framework adds support for automatic URL routing to Django, and provides you with a simple, quick and consistent way of wiring your view logic to a set of URLs.",
+ "location": "/api-guide/routers/#routers",
+ "text": "Resource routing allows you to quickly declare all of the common routes for a given resourceful controller. Instead of declaring separate routes for your index... a resourceful route declares them in a single line of code. Ruby on Rails Documentation Some Web frameworks such as Rails provide functionality for automatically determining how the URLs for an application should be mapped to the logic that deals with handling incoming requests. REST framework adds support for automatic URL routing to Django, and provides you with a simple, quick and consistent way of wiring your view logic to a set of URLs.",
"title": "Routers"
- },
+ },
{
- "location": "/api-guide/routers/#usage",
- "text": "Here's an example of a simple URL conf, that uses SimpleRouter . from rest_framework import routers\n\nrouter = routers.SimpleRouter()\nrouter.register(r'users', UserViewSet)\nrouter.register(r'accounts', AccountViewSet)\nurlpatterns = router.urls There are two mandatory arguments to the register() method: prefix - The URL prefix to use for this set of routes. viewset - The viewset class. Optionally, you may also specify an additional argument: basename - The base to use for the URL names that are created. If unset the basename will be automatically generated based on the queryset attribute of the viewset, if it has one. Note that if the viewset does not include a queryset attribute then you must set basename when registering the viewset. The example above would generate the following URL patterns: URL pattern: ^users/$ Name: 'user-list' URL pattern: ^users/{pk}/$ Name: 'user-detail' URL pattern: ^accounts/$ Name: 'account-list' URL pattern: ^accounts/{pk}/$ Name: 'account-detail' Note : The basename argument is used to specify the initial part of the view name pattern. In the example above, that's the user or account part. Typically you won't need to specify the basename argument, but if you have a viewset where you've defined a custom get_queryset method, then the viewset may not have a .queryset attribute set. If you try to register that viewset you'll see an error like this: 'basename' argument not specified, and could not automatically determine the name from the viewset, as it does not have a '.queryset' attribute. This means you'll need to explicitly set the basename argument when registering the viewset, as it could not be automatically determined from the model name.",
+ "location": "/api-guide/routers/#usage",
+ "text": "Here's an example of a simple URL conf, that uses SimpleRouter . from rest_framework import routers\n\nrouter = routers.SimpleRouter()\nrouter.register(r'users', UserViewSet)\nrouter.register(r'accounts', AccountViewSet)\nurlpatterns = router.urls There are two mandatory arguments to the register() method: prefix - The URL prefix to use for this set of routes. viewset - The viewset class. Optionally, you may also specify an additional argument: basename - The base to use for the URL names that are created. If unset the basename will be automatically generated based on the queryset attribute of the viewset, if it has one. Note that if the viewset does not include a queryset attribute then you must set basename when registering the viewset. The example above would generate the following URL patterns: URL pattern: ^users/$ Name: 'user-list' URL pattern: ^users/{pk}/$ Name: 'user-detail' URL pattern: ^accounts/$ Name: 'account-list' URL pattern: ^accounts/{pk}/$ Name: 'account-detail' Note : The basename argument is used to specify the initial part of the view name pattern. In the example above, that's the user or account part. Typically you won't need to specify the basename argument, but if you have a viewset where you've defined a custom get_queryset method, then the viewset may not have a .queryset attribute set. If you try to register that viewset you'll see an error like this: 'basename' argument not specified, and could not automatically determine the name from the viewset, as it does not have a '.queryset' attribute. This means you'll need to explicitly set the basename argument when registering the viewset, as it could not be automatically determined from the model name.",
"title": "Usage"
- },
+ },
{
- "location": "/api-guide/routers/#using-include-with-routers",
- "text": "The .urls attribute on a router instance is simply a standard list of URL patterns. There are a number of different styles for how you can include these URLs. For example, you can append router.urls to a list of existing views... router = routers.SimpleRouter()\nrouter.register(r'users', UserViewSet)\nrouter.register(r'accounts', AccountViewSet)\n\nurlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n]\n\nurlpatterns += router.urls Alternatively you can use Django's include function, like so... urlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^', include(router.urls)),\n] You may use include with an application namespace: urlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^api/', include((router.urls, 'app_name'))),\n] Or both an application and instance namespace: urlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^api/', include((router.urls, 'app_name'), namespace='instance_name')),\n] See Django's URL namespaces docs and the include API reference for more details. Note : If using namespacing with hyperlinked serializers you'll also need to ensure that any view_name parameters\non the serializers correctly reflect the namespace. In the examples above you'd need to include a parameter such as view_name='app_name:user-detail' for serializer fields hyperlinked to the user detail view. The automatic view_name generation uses a pattern like %(model_name)-detail . Unless your models names actually clash\nyou may be better off not namespacing your Django REST Framework views when using hyperlinked serializers.",
+ "location": "/api-guide/routers/#using-include-with-routers",
+ "text": "The .urls attribute on a router instance is simply a standard list of URL patterns. There are a number of different styles for how you can include these URLs. For example, you can append router.urls to a list of existing views... router = routers.SimpleRouter()\nrouter.register(r'users', UserViewSet)\nrouter.register(r'accounts', AccountViewSet)\n\nurlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n]\n\nurlpatterns += router.urls Alternatively you can use Django's include function, like so... urlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^', include(router.urls)),\n] You may use include with an application namespace: urlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^api/', include((router.urls, 'app_name'))),\n] Or both an application and instance namespace: urlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^api/', include((router.urls, 'app_name'), namespace='instance_name')),\n] See Django's URL namespaces docs and the include API reference for more details. Note : If using namespacing with hyperlinked serializers you'll also need to ensure that any view_name parameters\non the serializers correctly reflect the namespace. In the examples above you'd need to include a parameter such as view_name='app_name:user-detail' for serializer fields hyperlinked to the user detail view. The automatic view_name generation uses a pattern like %(model_name)-detail . Unless your models names actually clash\nyou may be better off not namespacing your Django REST Framework views when using hyperlinked serializers.",
"title": "Using include with routers"
- },
+ },
{
- "location": "/api-guide/routers/#routing-for-extra-actions",
- "text": "A viewset may mark extra actions for routing by decorating a method with the @action decorator. These extra actions will be included in the generated routes. For example, given the set_password method on the UserViewSet class: from myapp.permissions import IsAdminOrIsSelf\nfrom rest_framework.decorators import action\n\nclass UserViewSet(ModelViewSet):\n ...\n\n @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf])\n def set_password(self, request, pk=None):\n ... The following route would be generated: URL pattern: ^users/{pk}/set_password/$ URL name: 'user-set-password' By default, the URL pattern is based on the method name, and the URL name is the combination of the ViewSet.basename and the hyphenated method name.\nIf you don't want to use the defaults for either of these values, you can instead provide the url_path and url_name arguments to the @action decorator. For example, if you want to change the URL for our custom action to ^users/{pk}/change-password/$ , you could write: from myapp.permissions import IsAdminOrIsSelf\nfrom rest_framework.decorators import action\n\nclass UserViewSet(ModelViewSet):\n ...\n\n @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf],\n url_path='change-password', url_name='change_password')\n def set_password(self, request, pk=None):\n ... The above example would now generate the following URL pattern: URL path: ^users/{pk}/change-password/$ URL name: 'user-change_password'",
+ "location": "/api-guide/routers/#routing-for-extra-actions",
+ "text": "A viewset may mark extra actions for routing by decorating a method with the @action decorator. These extra actions will be included in the generated routes. For example, given the set_password method on the UserViewSet class: from myapp.permissions import IsAdminOrIsSelf\nfrom rest_framework.decorators import action\n\nclass UserViewSet(ModelViewSet):\n ...\n\n @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf])\n def set_password(self, request, pk=None):\n ... The following route would be generated: URL pattern: ^users/{pk}/set_password/$ URL name: 'user-set-password' By default, the URL pattern is based on the method name, and the URL name is the combination of the ViewSet.basename and the hyphenated method name.\nIf you don't want to use the defaults for either of these values, you can instead provide the url_path and url_name arguments to the @action decorator. For example, if you want to change the URL for our custom action to ^users/{pk}/change-password/$ , you could write: from myapp.permissions import IsAdminOrIsSelf\nfrom rest_framework.decorators import action\n\nclass UserViewSet(ModelViewSet):\n ...\n\n @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf],\n url_path='change-password', url_name='change_password')\n def set_password(self, request, pk=None):\n ... The above example would now generate the following URL pattern: URL path: ^users/{pk}/change-password/$ URL name: 'user-change_password'",
"title": "Routing for extra actions"
- },
+ },
{
- "location": "/api-guide/routers/#api-guide",
- "text": "",
+ "location": "/api-guide/routers/#api-guide",
+ "text": "",
"title": "API Guide"
- },
+ },
{
- "location": "/api-guide/routers/#simplerouter",
- "text": "This router includes routes for the standard set of list , create , retrieve , update , partial_update and destroy actions. The viewset can also mark additional methods to be routed, using the @action decorator. \n URL Style HTTP Method Action URL Name \n {prefix}/ GET list {basename}-list \n POST create \n {prefix}/{url_path}/ GET, or as specified by `methods` argument `@action(detail=False)` decorated method {basename}-{url_name} \n {prefix}/{lookup}/ GET retrieve {basename}-detail \n PUT update \n PATCH partial_update \n DELETE destroy \n {prefix}/{lookup}/{url_path}/ GET, or as specified by `methods` argument `@action(detail=True)` decorated method {basename}-{url_name} By default the URLs created by SimpleRouter are appended with a trailing slash.\nThis behavior can be modified by setting the trailing_slash argument to False when instantiating the router. For example: router = SimpleRouter(trailing_slash=False) Trailing slashes are conventional in Django, but are not used by default in some other frameworks such as Rails. Which style you choose to use is largely a matter of preference, although some javascript frameworks may expect a particular routing style. The router will match lookup values containing any characters except slashes and period characters. For a more restrictive (or lenient) lookup pattern, set the lookup_value_regex attribute on the viewset. For example, you can limit the lookup to valid UUIDs: class MyModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):\n lookup_field = 'my_model_id'\n lookup_value_regex = '[0-9a-f]{32}'",
+ "location": "/api-guide/routers/#simplerouter",
+ "text": "This router includes routes for the standard set of list , create , retrieve , update , partial_update and destroy actions. The viewset can also mark additional methods to be routed, using the @action decorator. \n URL Style HTTP Method Action URL Name \n {prefix}/ GET list {basename}-list \n POST create \n {prefix}/{url_path}/ GET, or as specified by `methods` argument `@action(detail=False)` decorated method {basename}-{url_name} \n {prefix}/{lookup}/ GET retrieve {basename}-detail \n PUT update \n PATCH partial_update \n DELETE destroy \n {prefix}/{lookup}/{url_path}/ GET, or as specified by `methods` argument `@action(detail=True)` decorated method {basename}-{url_name} By default the URLs created by SimpleRouter are appended with a trailing slash.\nThis behavior can be modified by setting the trailing_slash argument to False when instantiating the router. For example: router = SimpleRouter(trailing_slash=False) Trailing slashes are conventional in Django, but are not used by default in some other frameworks such as Rails. Which style you choose to use is largely a matter of preference, although some javascript frameworks may expect a particular routing style. The router will match lookup values containing any characters except slashes and period characters. For a more restrictive (or lenient) lookup pattern, set the lookup_value_regex attribute on the viewset. For example, you can limit the lookup to valid UUIDs: class MyModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):\n lookup_field = 'my_model_id'\n lookup_value_regex = '[0-9a-f]{32}'",
"title": "SimpleRouter"
- },
+ },
{
- "location": "/api-guide/routers/#defaultrouter",
- "text": "This router is similar to SimpleRouter as above, but additionally includes a default API root view, that returns a response containing hyperlinks to all the list views. It also generates routes for optional .json style format suffixes. \n URL Style HTTP Method Action URL Name \n [.format] GET automatically generated root view api-root \n {prefix}/[.format] GET list {basename}-list \n POST create \n {prefix}/{url_path}/[.format] GET, or as specified by `methods` argument `@action(detail=False)` decorated method {basename}-{url_name} \n {prefix}/{lookup}/[.format] GET retrieve {basename}-detail \n PUT update \n PATCH partial_update \n DELETE destroy \n {prefix}/{lookup}/{url_path}/[.format] GET, or as specified by `methods` argument `@action(detail=True)` decorated method {basename}-{url_name} As with SimpleRouter the trailing slashes on the URL routes can be removed by setting the trailing_slash argument to False when instantiating the router. router = DefaultRouter(trailing_slash=False)",
+ "location": "/api-guide/routers/#defaultrouter",
+ "text": "This router is similar to SimpleRouter as above, but additionally includes a default API root view, that returns a response containing hyperlinks to all the list views. It also generates routes for optional .json style format suffixes. \n URL Style HTTP Method Action URL Name \n [.format] GET automatically generated root view api-root \n {prefix}/[.format] GET list {basename}-list \n POST create \n {prefix}/{url_path}/[.format] GET, or as specified by `methods` argument `@action(detail=False)` decorated method {basename}-{url_name} \n {prefix}/{lookup}/[.format] GET retrieve {basename}-detail \n PUT update \n PATCH partial_update \n DELETE destroy \n {prefix}/{lookup}/{url_path}/[.format] GET, or as specified by `methods` argument `@action(detail=True)` decorated method {basename}-{url_name} As with SimpleRouter the trailing slashes on the URL routes can be removed by setting the trailing_slash argument to False when instantiating the router. router = DefaultRouter(trailing_slash=False)",
"title": "DefaultRouter"
- },
+ },
{
- "location": "/api-guide/routers/#custom-routers",
- "text": "Implementing a custom router isn't something you'd need to do very often, but it can be useful if you have specific requirements about how the URLs for your API are structured. Doing so allows you to encapsulate the URL structure in a reusable way that ensures you don't have to write your URL patterns explicitly for each new view. The simplest way to implement a custom router is to subclass one of the existing router classes. The .routes attribute is used to template the URL patterns that will be mapped to each viewset. The .routes attribute is a list of Route named tuples. The arguments to the Route named tuple are: url : A string representing the URL to be routed. May include the following format strings: {prefix} - The URL prefix to use for this set of routes. {lookup} - The lookup field used to match against a single instance. {trailing_slash} - Either a '/' or an empty string, depending on the trailing_slash argument. mapping : A mapping of HTTP method names to the view methods name : The name of the URL as used in reverse calls. May include the following format string: {basename} - The base to use for the URL names that are created. initkwargs : A dictionary of any additional arguments that should be passed when instantiating the view. Note that the detail , basename , and suffix arguments are reserved for viewset introspection and are also used by the browsable API to generate the view name and breadcrumb links.",
+ "location": "/api-guide/routers/#custom-routers",
+ "text": "Implementing a custom router isn't something you'd need to do very often, but it can be useful if you have specific requirements about how the URLs for your API are structured. Doing so allows you to encapsulate the URL structure in a reusable way that ensures you don't have to write your URL patterns explicitly for each new view. The simplest way to implement a custom router is to subclass one of the existing router classes. The .routes attribute is used to template the URL patterns that will be mapped to each viewset. The .routes attribute is a list of Route named tuples. The arguments to the Route named tuple are: url : A string representing the URL to be routed. May include the following format strings: {prefix} - The URL prefix to use for this set of routes. {lookup} - The lookup field used to match against a single instance. {trailing_slash} - Either a '/' or an empty string, depending on the trailing_slash argument. mapping : A mapping of HTTP method names to the view methods name : The name of the URL as used in reverse calls. May include the following format string: {basename} - The base to use for the URL names that are created. initkwargs : A dictionary of any additional arguments that should be passed when instantiating the view. Note that the detail , basename , and suffix arguments are reserved for viewset introspection and are also used by the browsable API to generate the view name and breadcrumb links.",
"title": "Custom Routers"
- },
+ },
{
- "location": "/api-guide/routers/#customizing-dynamic-routes",
- "text": "You can also customize how the @action decorator is routed. Include the DynamicRoute named tuple in the .routes list, setting the detail argument as appropriate for the list-based and detail-based routes. In addition to detail , the arguments to DynamicRoute are: url : A string representing the URL to be routed. May include the same format strings as Route , and additionally accepts the {url_path} format string. name : The name of the URL as used in reverse calls. May include the following format strings: {basename} - The base to use for the URL names that are created. {url_name} - The url_name provided to the @action . initkwargs : A dictionary of any additional arguments that should be passed when instantiating the view.",
+ "location": "/api-guide/routers/#customizing-dynamic-routes",
+ "text": "You can also customize how the @action decorator is routed. Include the DynamicRoute named tuple in the .routes list, setting the detail argument as appropriate for the list-based and detail-based routes. In addition to detail , the arguments to DynamicRoute are: url : A string representing the URL to be routed. May include the same format strings as Route , and additionally accepts the {url_path} format string. name : The name of the URL as used in reverse calls. May include the following format strings: {basename} - The base to use for the URL names that are created. {url_name} - The url_name provided to the @action . initkwargs : A dictionary of any additional arguments that should be passed when instantiating the view.",
"title": "Customizing dynamic routes"
- },
+ },
{
- "location": "/api-guide/routers/#example",
- "text": "The following example will only route to the list and retrieve actions, and does not use the trailing slash convention. from rest_framework.routers import Route, DynamicRoute, SimpleRouter\n\nclass CustomReadOnlyRouter(SimpleRouter):\n \"\"\"\n A router for read-only APIs, which doesn't use trailing slashes.\n \"\"\"\n routes = [\n Route(\n url=r'^{prefix}$',\n mapping={'get': 'list'},\n name='{basename}-list',\n detail=False,\n initkwargs={'suffix': 'List'}\n ),\n Route(\n url=r'^{prefix}/{lookup}$',\n mapping={'get': 'retrieve'},\n name='{basename}-detail',\n detail=True,\n initkwargs={'suffix': 'Detail'}\n ),\n DynamicRoute(\n url=r'^{prefix}/{lookup}/{url_path}$',\n name='{basename}-{url_name}',\n detail=True,\n initkwargs={}\n )\n ] Let's take a look at the routes our CustomReadOnlyRouter would generate for a simple viewset. views.py : class UserViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n A viewset that provides the standard actions\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer\n lookup_field = 'username'\n\n @action(detail=True)\n def group_names(self, request, pk=None):\n \"\"\"\n Returns a list of all the group names that the given\n user belongs to.\n \"\"\"\n user = self.get_object()\n groups = user.groups.all()\n return Response([group.name for group in groups]) urls.py : router = CustomReadOnlyRouter()\nrouter.register('users', UserViewSet)\nurlpatterns = router.urls The following mappings would be generated... \n URL HTTP Method Action URL Name \n /users GET list user-list \n /users/{username} GET retrieve user-detail \n /users/{username}/group_names GET group_names user-group-names For another example of setting the .routes attribute, see the source code for the SimpleRouter class.",
+ "location": "/api-guide/routers/#example",
+ "text": "The following example will only route to the list and retrieve actions, and does not use the trailing slash convention. from rest_framework.routers import Route, DynamicRoute, SimpleRouter\n\nclass CustomReadOnlyRouter(SimpleRouter):\n \"\"\"\n A router for read-only APIs, which doesn't use trailing slashes.\n \"\"\"\n routes = [\n Route(\n url=r'^{prefix}$',\n mapping={'get': 'list'},\n name='{basename}-list',\n detail=False,\n initkwargs={'suffix': 'List'}\n ),\n Route(\n url=r'^{prefix}/{lookup}$',\n mapping={'get': 'retrieve'},\n name='{basename}-detail',\n detail=True,\n initkwargs={'suffix': 'Detail'}\n ),\n DynamicRoute(\n url=r'^{prefix}/{lookup}/{url_path}$',\n name='{basename}-{url_name}',\n detail=True,\n initkwargs={}\n )\n ] Let's take a look at the routes our CustomReadOnlyRouter would generate for a simple viewset. views.py : class UserViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n A viewset that provides the standard actions\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer\n lookup_field = 'username'\n\n @action(detail=True)\n def group_names(self, request, pk=None):\n \"\"\"\n Returns a list of all the group names that the given\n user belongs to.\n \"\"\"\n user = self.get_object()\n groups = user.groups.all()\n return Response([group.name for group in groups]) urls.py : router = CustomReadOnlyRouter()\nrouter.register('users', UserViewSet)\nurlpatterns = router.urls The following mappings would be generated... \n URL HTTP Method Action URL Name \n /users GET list user-list \n /users/{username} GET retrieve user-detail \n /users/{username}/group_names GET group_names user-group-names For another example of setting the .routes attribute, see the source code for the SimpleRouter class.",
"title": "Example"
- },
+ },
{
- "location": "/api-guide/routers/#advanced-custom-routers",
- "text": "If you want to provide totally custom behavior, you can override BaseRouter and override the get_urls(self) method. The method should inspect the registered viewsets and return a list of URL patterns. The registered prefix, viewset and basename tuples may be inspected by accessing the self.registry attribute. You may also want to override the get_default_basename(self, viewset) method, or else always explicitly set the basename argument when registering your viewsets with the router.",
+ "location": "/api-guide/routers/#advanced-custom-routers",
+ "text": "If you want to provide totally custom behavior, you can override BaseRouter and override the get_urls(self) method. The method should inspect the registered viewsets and return a list of URL patterns. The registered prefix, viewset and basename tuples may be inspected by accessing the self.registry attribute. You may also want to override the get_default_basename(self, viewset) method, or else always explicitly set the basename argument when registering your viewsets with the router.",
"title": "Advanced custom routers"
- },
+ },
{
- "location": "/api-guide/routers/#third-party-packages",
- "text": "The following third party packages are also available.",
+ "location": "/api-guide/routers/#third-party-packages",
+ "text": "The following third party packages are also available.",
"title": "Third Party Packages"
- },
+ },
{
- "location": "/api-guide/routers/#drf-nested-routers",
- "text": "The drf-nested-routers package provides routers and relationship fields for working with nested resources.",
+ "location": "/api-guide/routers/#drf-nested-routers",
+ "text": "The drf-nested-routers package provides routers and relationship fields for working with nested resources.",
"title": "DRF Nested Routers"
- },
+ },
{
- "location": "/api-guide/routers/#modelrouter-wqdbrest",
- "text": "The wq.db package provides an advanced ModelRouter class (and singleton instance) that extends DefaultRouter with a register_model() API. Much like Django's admin.site.register , the only required argument to rest.router.register_model is a model class. Reasonable defaults for a url prefix, serializer, and viewset will be inferred from the model and global configuration. from wq.db import rest\nfrom myapp.models import MyModel\n\nrest.router.register_model(MyModel)",
+ "location": "/api-guide/routers/#modelrouter-wqdbrest",
+ "text": "The wq.db package provides an advanced ModelRouter class (and singleton instance) that extends DefaultRouter with a register_model() API. Much like Django's admin.site.register , the only required argument to rest.router.register_model is a model class. Reasonable defaults for a url prefix, serializer, and viewset will be inferred from the model and global configuration. from wq.db import rest\nfrom myapp.models import MyModel\n\nrest.router.register_model(MyModel)",
"title": "ModelRouter (wq.db.rest)"
- },
+ },
{
- "location": "/api-guide/routers/#drf-extensions",
- "text": "The DRF-extensions package provides routers for creating nested viewsets , collection level controllers with customizable endpoint names .",
+ "location": "/api-guide/routers/#drf-extensions",
+ "text": "The DRF-extensions package provides routers for creating nested viewsets , collection level controllers with customizable endpoint names .",
"title": "DRF-extensions"
- },
+ },
{
- "location": "/api-guide/parsers/",
- "text": "Parsers\n\n\n\n\nMachine interacting web services tend to use more\nstructured formats for sending data than form-encoded, since they're\nsending more complex data than simple forms\n\n\n\u2014 Malcom Tredinnick, \nDjango developers group\n\n\n\n\nREST framework includes a number of built in Parser classes, that allow you to accept requests with various media types. There is also support for defining your own custom parsers, which gives you the flexibility to design the media types that your API accepts.\n\n\nHow the parser is determined\n\n\nThe set of valid parsers for a view is always defined as a list of classes. When \nrequest.data\n is accessed, REST framework will examine the \nContent-Type\n header on the incoming request, and determine which parser to use to parse the request content.\n\n\n\n\nNote\n: When developing client applications always remember to make sure you're setting the \nContent-Type\n header when sending data in an HTTP request.\n\n\nIf you don't set the content type, most clients will default to using \n'application/x-www-form-urlencoded'\n, which may not be what you wanted.\n\n\nAs an example, if you are sending \njson\n encoded data using jQuery with the \n.ajax() method\n, you should make sure to include the \ncontentType: 'application/json'\n setting.\n\n\n\n\nSetting the parsers\n\n\nThe default set of parsers may be set globally, using the \nDEFAULT_PARSER_CLASSES\n setting. For example, the following settings would allow only requests with \nJSON\n content, instead of the default of JSON or form data.\n\n\nREST_FRAMEWORK = {\n 'DEFAULT_PARSER_CLASSES': (\n 'rest_framework.parsers.JSONParser',\n )\n}\n\n\n\nYou can also set the parsers used for an individual view, or viewset,\nusing the \nAPIView\n class-based views.\n\n\nfrom rest_framework.parsers import JSONParser\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\n\nclass ExampleView(APIView):\n \"\"\"\n A view that can accept POST requests with JSON content.\n \"\"\"\n parser_classes = (JSONParser,)\n\n def post(self, request, format=None):\n return Response({'received data': request.data})\n\n\n\nOr, if you're using the \n@api_view\n decorator with function based views.\n\n\nfrom rest_framework.decorators import api_view\nfrom rest_framework.decorators import parser_classes\nfrom rest_framework.parsers import JSONParser\n\n@api_view(['POST'])\n@parser_classes((JSONParser,))\ndef example_view(request, format=None):\n \"\"\"\n A view that can accept POST requests with JSON content.\n \"\"\"\n return Response({'received data': request.data})\n\n\n\n\n\nAPI Reference\n\n\nJSONParser\n\n\nParses \nJSON\n request content.\n\n\n.media_type\n: \napplication/json\n\n\nFormParser\n\n\nParses HTML form content. \nrequest.data\n will be populated with a \nQueryDict\n of data.\n\n\nYou will typically want to use both \nFormParser\n and \nMultiPartParser\n together in order to fully support HTML form data.\n\n\n.media_type\n: \napplication/x-www-form-urlencoded\n\n\nMultiPartParser\n\n\nParses multipart HTML form content, which supports file uploads. Both \nrequest.data\n will be populated with a \nQueryDict\n.\n\n\nYou will typically want to use both \nFormParser\n and \nMultiPartParser\n together in order to fully support HTML form data.\n\n\n.media_type\n: \nmultipart/form-data\n\n\nFileUploadParser\n\n\nParses raw file upload content. The \nrequest.data\n property will be a dictionary with a single key \n'file'\n containing the uploaded file.\n\n\nIf the view used with \nFileUploadParser\n is called with a \nfilename\n URL keyword argument, then that argument will be used as the filename.\n\n\nIf it is called without a \nfilename\n URL keyword argument, then the client must set the filename in the \nContent-Disposition\n HTTP header. For example \nContent-Disposition: attachment; filename=upload.jpg\n.\n\n\n.media_type\n: \n*/*\n\n\nNotes:\n\n\n\n\nThe \nFileUploadParser\n is for usage with native clients that can upload the file as a raw data request. For web-based uploads, or for native clients with multipart upload support, you should use the \nMultiPartParser\n instead.\n\n\nSince this parser's \nmedia_type\n matches any content type, \nFileUploadParser\n should generally be the only parser set on an API view.\n\n\nFileUploadParser\n respects Django's standard \nFILE_UPLOAD_HANDLERS\n setting, and the \nrequest.upload_handlers\n attribute. See the \nDjango documentation\n for more details.\n\n\n\n\nBasic usage example:\n\n\n# views.py\nclass FileUploadView(views.APIView):\n parser_classes = (FileUploadParser,)\n\n def put(self, request, filename, format=None):\n file_obj = request.data['file']\n # ...\n # do some stuff with uploaded file\n # ...\n return Response(status=204)\n\n# urls.py\nurlpatterns = [\n # ...\n url(r'^upload/(?P[^/]+)$', FileUploadView.as_view())\n]\n\n\n\n\n\nCustom parsers\n\n\nTo implement a custom parser, you should override \nBaseParser\n, set the \n.media_type\n property, and implement the \n.parse(self, stream, media_type, parser_context)\n method.\n\n\nThe method should return the data that will be used to populate the \nrequest.data\n property.\n\n\nThe arguments passed to \n.parse()\n are:\n\n\nstream\n\n\nA stream-like object representing the body of the request.\n\n\nmedia_type\n\n\nOptional. If provided, this is the media type of the incoming request content.\n\n\nDepending on the request's \nContent-Type:\n header, this may be more specific than the renderer's \nmedia_type\n attribute, and may include media type parameters. For example \n\"text/plain; charset=utf-8\"\n.\n\n\nparser_context\n\n\nOptional. If supplied, this argument will be a dictionary containing any additional context that may be required to parse the request content.\n\n\nBy default this will include the following keys: \nview\n, \nrequest\n, \nargs\n, \nkwargs\n.\n\n\nExample\n\n\nThe following is an example plaintext parser that will populate the \nrequest.data\n property with a string representing the body of the request.\n\n\nclass PlainTextParser(BaseParser):\n \"\"\"\n Plain text parser.\n \"\"\"\n media_type = 'text/plain'\n\n def parse(self, stream, media_type=None, parser_context=None):\n \"\"\"\n Simply return a string representing the body of the request.\n \"\"\"\n return stream.read()\n\n\n\n\n\nThird party packages\n\n\nThe following third party packages are also available.\n\n\nYAML\n\n\nREST framework YAML\n provides \nYAML\n parsing and rendering support. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.\n\n\nInstallation & configuration\n\n\nInstall using pip.\n\n\n$ pip install djangorestframework-yaml\n\n\n\nModify your REST framework settings.\n\n\nREST_FRAMEWORK = {\n 'DEFAULT_PARSER_CLASSES': (\n 'rest_framework_yaml.parsers.YAMLParser',\n ),\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework_yaml.renderers.YAMLRenderer',\n ),\n}\n\n\n\nXML\n\n\nREST Framework XML\n provides a simple informal XML format. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.\n\n\nInstallation & configuration\n\n\nInstall using pip.\n\n\n$ pip install djangorestframework-xml\n\n\n\nModify your REST framework settings.\n\n\nREST_FRAMEWORK = {\n 'DEFAULT_PARSER_CLASSES': (\n 'rest_framework_xml.parsers.XMLParser',\n ),\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework_xml.renderers.XMLRenderer',\n ),\n}\n\n\n\nMessagePack\n\n\nMessagePack\n is a fast, efficient binary serialization format. \nJuan Riaza\n maintains the \ndjangorestframework-msgpack\n package which provides MessagePack renderer and parser support for REST framework.\n\n\nCamelCase JSON\n\n\ndjangorestframework-camel-case\n provides camel case JSON renderers and parsers for REST framework. This allows serializers to use Python-style underscored field names, but be exposed in the API as Javascript-style camel case field names. It is maintained by \nVitaly Babiy\n.",
+ "location": "/api-guide/parsers/",
+ "text": "Parsers\n\n\n\n\nMachine interacting web services tend to use more\nstructured formats for sending data than form-encoded, since they're\nsending more complex data than simple forms\n\n\n Malcom Tredinnick, \nDjango developers group\n\n\n\n\nREST framework includes a number of built in Parser classes, that allow you to accept requests with various media types. There is also support for defining your own custom parsers, which gives you the flexibility to design the media types that your API accepts.\n\n\nHow the parser is determined\n\n\nThe set of valid parsers for a view is always defined as a list of classes. When \nrequest.data\n is accessed, REST framework will examine the \nContent-Type\n header on the incoming request, and determine which parser to use to parse the request content.\n\n\n\n\nNote\n: When developing client applications always remember to make sure you're setting the \nContent-Type\n header when sending data in an HTTP request.\n\n\nIf you don't set the content type, most clients will default to using \n'application/x-www-form-urlencoded'\n, which may not be what you wanted.\n\n\nAs an example, if you are sending \njson\n encoded data using jQuery with the \n.ajax() method\n, you should make sure to include the \ncontentType: 'application/json'\n setting.\n\n\n\n\nSetting the parsers\n\n\nThe default set of parsers may be set globally, using the \nDEFAULT_PARSER_CLASSES\n setting. For example, the following settings would allow only requests with \nJSON\n content, instead of the default of JSON or form data.\n\n\nREST_FRAMEWORK = {\n 'DEFAULT_PARSER_CLASSES': (\n 'rest_framework.parsers.JSONParser',\n )\n}\n\n\n\nYou can also set the parsers used for an individual view, or viewset,\nusing the \nAPIView\n class-based views.\n\n\nfrom rest_framework.parsers import JSONParser\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\n\nclass ExampleView(APIView):\n \"\"\"\n A view that can accept POST requests with JSON content.\n \"\"\"\n parser_classes = (JSONParser,)\n\n def post(self, request, format=None):\n return Response({'received data': request.data})\n\n\n\nOr, if you're using the \n@api_view\n decorator with function based views.\n\n\nfrom rest_framework.decorators import api_view\nfrom rest_framework.decorators import parser_classes\nfrom rest_framework.parsers import JSONParser\n\n@api_view(['POST'])\n@parser_classes((JSONParser,))\ndef example_view(request, format=None):\n \"\"\"\n A view that can accept POST requests with JSON content.\n \"\"\"\n return Response({'received data': request.data})\n\n\n\n\n\nAPI Reference\n\n\nJSONParser\n\n\nParses \nJSON\n request content.\n\n\n.media_type\n: \napplication/json\n\n\nFormParser\n\n\nParses HTML form content. \nrequest.data\n will be populated with a \nQueryDict\n of data.\n\n\nYou will typically want to use both \nFormParser\n and \nMultiPartParser\n together in order to fully support HTML form data.\n\n\n.media_type\n: \napplication/x-www-form-urlencoded\n\n\nMultiPartParser\n\n\nParses multipart HTML form content, which supports file uploads. Both \nrequest.data\n will be populated with a \nQueryDict\n.\n\n\nYou will typically want to use both \nFormParser\n and \nMultiPartParser\n together in order to fully support HTML form data.\n\n\n.media_type\n: \nmultipart/form-data\n\n\nFileUploadParser\n\n\nParses raw file upload content. The \nrequest.data\n property will be a dictionary with a single key \n'file'\n containing the uploaded file.\n\n\nIf the view used with \nFileUploadParser\n is called with a \nfilename\n URL keyword argument, then that argument will be used as the filename.\n\n\nIf it is called without a \nfilename\n URL keyword argument, then the client must set the filename in the \nContent-Disposition\n HTTP header. For example \nContent-Disposition: attachment; filename=upload.jpg\n.\n\n\n.media_type\n: \n*/*\n\n\nNotes:\n\n\n\n\nThe \nFileUploadParser\n is for usage with native clients that can upload the file as a raw data request. For web-based uploads, or for native clients with multipart upload support, you should use the \nMultiPartParser\n instead.\n\n\nSince this parser's \nmedia_type\n matches any content type, \nFileUploadParser\n should generally be the only parser set on an API view.\n\n\nFileUploadParser\n respects Django's standard \nFILE_UPLOAD_HANDLERS\n setting, and the \nrequest.upload_handlers\n attribute. See the \nDjango documentation\n for more details.\n\n\n\n\nBasic usage example:\n\n\n# views.py\nclass FileUploadView(views.APIView):\n parser_classes = (FileUploadParser,)\n\n def put(self, request, filename, format=None):\n file_obj = request.data['file']\n # ...\n # do some stuff with uploaded file\n # ...\n return Response(status=204)\n\n# urls.py\nurlpatterns = [\n # ...\n url(r'^upload/(?P\nfilename\n[^/]+)$', FileUploadView.as_view())\n]\n\n\n\n\n\nCustom parsers\n\n\nTo implement a custom parser, you should override \nBaseParser\n, set the \n.media_type\n property, and implement the \n.parse(self, stream, media_type, parser_context)\n method.\n\n\nThe method should return the data that will be used to populate the \nrequest.data\n property.\n\n\nThe arguments passed to \n.parse()\n are:\n\n\nstream\n\n\nA stream-like object representing the body of the request.\n\n\nmedia_type\n\n\nOptional. If provided, this is the media type of the incoming request content.\n\n\nDepending on the request's \nContent-Type:\n header, this may be more specific than the renderer's \nmedia_type\n attribute, and may include media type parameters. For example \n\"text/plain; charset=utf-8\"\n.\n\n\nparser_context\n\n\nOptional. If supplied, this argument will be a dictionary containing any additional context that may be required to parse the request content.\n\n\nBy default this will include the following keys: \nview\n, \nrequest\n, \nargs\n, \nkwargs\n.\n\n\nExample\n\n\nThe following is an example plaintext parser that will populate the \nrequest.data\n property with a string representing the body of the request.\n\n\nclass PlainTextParser(BaseParser):\n \"\"\"\n Plain text parser.\n \"\"\"\n media_type = 'text/plain'\n\n def parse(self, stream, media_type=None, parser_context=None):\n \"\"\"\n Simply return a string representing the body of the request.\n \"\"\"\n return stream.read()\n\n\n\n\n\nThird party packages\n\n\nThe following third party packages are also available.\n\n\nYAML\n\n\nREST framework YAML\n provides \nYAML\n parsing and rendering support. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.\n\n\nInstallation \n configuration\n\n\nInstall using pip.\n\n\n$ pip install djangorestframework-yaml\n\n\n\nModify your REST framework settings.\n\n\nREST_FRAMEWORK = {\n 'DEFAULT_PARSER_CLASSES': (\n 'rest_framework_yaml.parsers.YAMLParser',\n ),\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework_yaml.renderers.YAMLRenderer',\n ),\n}\n\n\n\nXML\n\n\nREST Framework XML\n provides a simple informal XML format. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.\n\n\nInstallation \n configuration\n\n\nInstall using pip.\n\n\n$ pip install djangorestframework-xml\n\n\n\nModify your REST framework settings.\n\n\nREST_FRAMEWORK = {\n 'DEFAULT_PARSER_CLASSES': (\n 'rest_framework_xml.parsers.XMLParser',\n ),\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework_xml.renderers.XMLRenderer',\n ),\n}\n\n\n\nMessagePack\n\n\nMessagePack\n is a fast, efficient binary serialization format. \nJuan Riaza\n maintains the \ndjangorestframework-msgpack\n package which provides MessagePack renderer and parser support for REST framework.\n\n\nCamelCase JSON\n\n\ndjangorestframework-camel-case\n provides camel case JSON renderers and parsers for REST framework. This allows serializers to use Python-style underscored field names, but be exposed in the API as Javascript-style camel case field names. It is maintained by \nVitaly Babiy\n.",
"title": "Parsers"
- },
+ },
{
- "location": "/api-guide/parsers/#parsers",
- "text": "Machine interacting web services tend to use more\nstructured formats for sending data than form-encoded, since they're\nsending more complex data than simple forms \u2014 Malcom Tredinnick, Django developers group REST framework includes a number of built in Parser classes, that allow you to accept requests with various media types. There is also support for defining your own custom parsers, which gives you the flexibility to design the media types that your API accepts.",
+ "location": "/api-guide/parsers/#parsers",
+ "text": "Machine interacting web services tend to use more\nstructured formats for sending data than form-encoded, since they're\nsending more complex data than simple forms Malcom Tredinnick, Django developers group REST framework includes a number of built in Parser classes, that allow you to accept requests with various media types. There is also support for defining your own custom parsers, which gives you the flexibility to design the media types that your API accepts.",
"title": "Parsers"
- },
+ },
{
- "location": "/api-guide/parsers/#how-the-parser-is-determined",
- "text": "The set of valid parsers for a view is always defined as a list of classes. When request.data is accessed, REST framework will examine the Content-Type header on the incoming request, and determine which parser to use to parse the request content. Note : When developing client applications always remember to make sure you're setting the Content-Type header when sending data in an HTTP request. If you don't set the content type, most clients will default to using 'application/x-www-form-urlencoded' , which may not be what you wanted. As an example, if you are sending json encoded data using jQuery with the .ajax() method , you should make sure to include the contentType: 'application/json' setting.",
+ "location": "/api-guide/parsers/#how-the-parser-is-determined",
+ "text": "The set of valid parsers for a view is always defined as a list of classes. When request.data is accessed, REST framework will examine the Content-Type header on the incoming request, and determine which parser to use to parse the request content. Note : When developing client applications always remember to make sure you're setting the Content-Type header when sending data in an HTTP request. If you don't set the content type, most clients will default to using 'application/x-www-form-urlencoded' , which may not be what you wanted. As an example, if you are sending json encoded data using jQuery with the .ajax() method , you should make sure to include the contentType: 'application/json' setting.",
"title": "How the parser is determined"
- },
+ },
{
- "location": "/api-guide/parsers/#setting-the-parsers",
- "text": "The default set of parsers may be set globally, using the DEFAULT_PARSER_CLASSES setting. For example, the following settings would allow only requests with JSON content, instead of the default of JSON or form data. REST_FRAMEWORK = {\n 'DEFAULT_PARSER_CLASSES': (\n 'rest_framework.parsers.JSONParser',\n )\n} You can also set the parsers used for an individual view, or viewset,\nusing the APIView class-based views. from rest_framework.parsers import JSONParser\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\n\nclass ExampleView(APIView):\n \"\"\"\n A view that can accept POST requests with JSON content.\n \"\"\"\n parser_classes = (JSONParser,)\n\n def post(self, request, format=None):\n 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\nfrom rest_framework.decorators import parser_classes\nfrom rest_framework.parsers import JSONParser\n\n@api_view(['POST'])\n@parser_classes((JSONParser,))\ndef example_view(request, format=None):\n \"\"\"\n A view that can accept POST requests with JSON content.\n \"\"\"\n return Response({'received data': request.data})",
+ "location": "/api-guide/parsers/#setting-the-parsers",
+ "text": "The default set of parsers may be set globally, using the DEFAULT_PARSER_CLASSES setting. For example, the following settings would allow only requests with JSON content, instead of the default of JSON or form data. REST_FRAMEWORK = {\n 'DEFAULT_PARSER_CLASSES': (\n 'rest_framework.parsers.JSONParser',\n )\n} You can also set the parsers used for an individual view, or viewset,\nusing the APIView class-based views. from rest_framework.parsers import JSONParser\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\n\nclass ExampleView(APIView):\n \"\"\"\n A view that can accept POST requests with JSON content.\n \"\"\"\n parser_classes = (JSONParser,)\n\n def post(self, request, format=None):\n 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\nfrom rest_framework.decorators import parser_classes\nfrom rest_framework.parsers import JSONParser\n\n@api_view(['POST'])\n@parser_classes((JSONParser,))\ndef example_view(request, format=None):\n \"\"\"\n A view that can accept POST requests with JSON content.\n \"\"\"\n return Response({'received data': request.data})",
"title": "Setting the parsers"
- },
+ },
{
- "location": "/api-guide/parsers/#api-reference",
- "text": "",
+ "location": "/api-guide/parsers/#api-reference",
+ "text": "",
"title": "API Reference"
- },
+ },
{
- "location": "/api-guide/parsers/#jsonparser",
- "text": "Parses JSON request content. .media_type : application/json",
+ "location": "/api-guide/parsers/#jsonparser",
+ "text": "Parses JSON request content. .media_type : application/json",
"title": "JSONParser"
- },
+ },
{
- "location": "/api-guide/parsers/#formparser",
- "text": "Parses HTML form content. request.data will be populated with a QueryDict of data. You will typically want to use both FormParser and MultiPartParser together in order to fully support HTML form data. .media_type : application/x-www-form-urlencoded",
+ "location": "/api-guide/parsers/#formparser",
+ "text": "Parses HTML form content. request.data will be populated with a QueryDict of data. You will typically want to use both FormParser and MultiPartParser together in order to fully support HTML form data. .media_type : application/x-www-form-urlencoded",
"title": "FormParser"
- },
+ },
{
- "location": "/api-guide/parsers/#multipartparser",
- "text": "Parses multipart HTML form content, which supports file uploads. Both request.data will be populated with a QueryDict . You will typically want to use both FormParser and MultiPartParser together in order to fully support HTML form data. .media_type : multipart/form-data",
+ "location": "/api-guide/parsers/#multipartparser",
+ "text": "Parses multipart HTML form content, which supports file uploads. Both request.data will be populated with a QueryDict . You will typically want to use both FormParser and MultiPartParser together in order to fully support HTML form data. .media_type : multipart/form-data",
"title": "MultiPartParser"
- },
+ },
{
- "location": "/api-guide/parsers/#fileuploadparser",
- "text": "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 . .media_type : */*",
+ "location": "/api-guide/parsers/#fileuploadparser",
+ "text": "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 . .media_type : */*",
"title": "FileUploadParser"
- },
+ },
{
- "location": "/api-guide/parsers/#notes",
- "text": "The FileUploadParser is for usage with native clients that can upload the file as a raw data request. For web-based uploads, or for native clients with multipart upload support, you should use the MultiPartParser instead. Since this parser's media_type matches any content type, FileUploadParser should generally be the only parser set on an API view. FileUploadParser respects Django's standard FILE_UPLOAD_HANDLERS setting, and the request.upload_handlers attribute. See the Django documentation for more details.",
+ "location": "/api-guide/parsers/#notes",
+ "text": "The FileUploadParser is for usage with native clients that can upload the file as a raw data request. For web-based uploads, or for native clients with multipart upload support, you should use the MultiPartParser instead. Since this parser's media_type matches any content type, FileUploadParser should generally be the only parser set on an API view. FileUploadParser respects Django's standard FILE_UPLOAD_HANDLERS setting, and the request.upload_handlers attribute. See the Django documentation for more details.",
"title": "Notes:"
- },
+ },
{
- "location": "/api-guide/parsers/#basic-usage-example",
- "text": "# views.py\nclass FileUploadView(views.APIView):\n parser_classes = (FileUploadParser,)\n\n def put(self, request, filename, format=None):\n file_obj = request.data['file']\n # ...\n # do some stuff with uploaded file\n # ...\n return Response(status=204)\n\n# urls.py\nurlpatterns = [\n # ...\n url(r'^upload/(?P[^/]+)$', FileUploadView.as_view())\n]",
+ "location": "/api-guide/parsers/#basic-usage-example",
+ "text": "# views.py\nclass FileUploadView(views.APIView):\n parser_classes = (FileUploadParser,)\n\n def put(self, request, filename, format=None):\n file_obj = request.data['file']\n # ...\n # do some stuff with uploaded file\n # ...\n return Response(status=204)\n\n# urls.py\nurlpatterns = [\n # ...\n url(r'^upload/(?P filename [^/]+)$', FileUploadView.as_view())\n]",
"title": "Basic usage example:"
- },
+ },
{
- "location": "/api-guide/parsers/#custom-parsers",
- "text": "To implement a custom parser, you should override BaseParser , set the .media_type property, and implement the .parse(self, stream, media_type, parser_context) method. The method should return the data that will be used to populate the request.data property. The arguments passed to .parse() are:",
+ "location": "/api-guide/parsers/#custom-parsers",
+ "text": "To implement a custom parser, you should override BaseParser , set the .media_type property, and implement the .parse(self, stream, media_type, parser_context) method. The method should return the data that will be used to populate the request.data property. The arguments passed to .parse() are:",
"title": "Custom parsers"
- },
+ },
{
- "location": "/api-guide/parsers/#stream",
- "text": "A stream-like object representing the body of the request.",
+ "location": "/api-guide/parsers/#stream",
+ "text": "A stream-like object representing the body of the request.",
"title": "stream"
- },
+ },
{
- "location": "/api-guide/parsers/#media_type",
- "text": "Optional. If provided, this is the media type of the incoming request content. Depending on the request's Content-Type: header, this may be more specific than the renderer's media_type attribute, and may include media type parameters. For example \"text/plain; charset=utf-8\" .",
+ "location": "/api-guide/parsers/#media_type",
+ "text": "Optional. If provided, this is the media type of the incoming request content. Depending on the request's Content-Type: header, this may be more specific than the renderer's media_type attribute, and may include media type parameters. For example \"text/plain; charset=utf-8\" .",
"title": "media_type"
- },
+ },
{
- "location": "/api-guide/parsers/#parser_context",
- "text": "Optional. If supplied, this argument will be a dictionary containing any additional context that may be required to parse the request content. By default this will include the following keys: view , request , args , kwargs .",
+ "location": "/api-guide/parsers/#parser_context",
+ "text": "Optional. If supplied, this argument will be a dictionary containing any additional context that may be required to parse the request content. By default this will include the following keys: view , request , args , kwargs .",
"title": "parser_context"
- },
+ },
{
- "location": "/api-guide/parsers/#example",
- "text": "The following is an example plaintext parser that will populate the request.data property with a string representing the body of the request. class PlainTextParser(BaseParser):\n \"\"\"\n Plain text parser.\n \"\"\"\n media_type = 'text/plain'\n\n def parse(self, stream, media_type=None, parser_context=None):\n \"\"\"\n Simply return a string representing the body of the request.\n \"\"\"\n return stream.read()",
+ "location": "/api-guide/parsers/#example",
+ "text": "The following is an example plaintext parser that will populate the request.data property with a string representing the body of the request. class PlainTextParser(BaseParser):\n \"\"\"\n Plain text parser.\n \"\"\"\n media_type = 'text/plain'\n\n def parse(self, stream, media_type=None, parser_context=None):\n \"\"\"\n Simply return a string representing the body of the request.\n \"\"\"\n return stream.read()",
"title": "Example"
- },
+ },
{
- "location": "/api-guide/parsers/#third-party-packages",
- "text": "The following third party packages are also available.",
+ "location": "/api-guide/parsers/#third-party-packages",
+ "text": "The following third party packages are also available.",
"title": "Third party packages"
- },
+ },
{
- "location": "/api-guide/parsers/#yaml",
- "text": "REST framework YAML provides YAML parsing and rendering support. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.",
+ "location": "/api-guide/parsers/#yaml",
+ "text": "REST framework YAML provides YAML parsing and rendering support. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.",
"title": "YAML"
- },
+ },
{
- "location": "/api-guide/parsers/#installation-configuration",
- "text": "Install using pip. $ pip install djangorestframework-yaml Modify your REST framework settings. REST_FRAMEWORK = {\n 'DEFAULT_PARSER_CLASSES': (\n 'rest_framework_yaml.parsers.YAMLParser',\n ),\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework_yaml.renderers.YAMLRenderer',\n ),\n}",
+ "location": "/api-guide/parsers/#installation-configuration",
+ "text": "Install using pip. $ pip install djangorestframework-yaml Modify your REST framework settings. REST_FRAMEWORK = {\n 'DEFAULT_PARSER_CLASSES': (\n 'rest_framework_yaml.parsers.YAMLParser',\n ),\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework_yaml.renderers.YAMLRenderer',\n ),\n}",
"title": "Installation & configuration"
- },
+ },
{
- "location": "/api-guide/parsers/#xml",
- "text": "REST Framework XML provides a simple informal XML format. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.",
+ "location": "/api-guide/parsers/#xml",
+ "text": "REST Framework XML provides a simple informal XML format. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.",
"title": "XML"
- },
+ },
{
- "location": "/api-guide/parsers/#installation-configuration_1",
- "text": "Install using pip. $ pip install djangorestframework-xml Modify your REST framework settings. REST_FRAMEWORK = {\n 'DEFAULT_PARSER_CLASSES': (\n 'rest_framework_xml.parsers.XMLParser',\n ),\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework_xml.renderers.XMLRenderer',\n ),\n}",
+ "location": "/api-guide/parsers/#installation-configuration_1",
+ "text": "Install using pip. $ pip install djangorestframework-xml Modify your REST framework settings. REST_FRAMEWORK = {\n 'DEFAULT_PARSER_CLASSES': (\n 'rest_framework_xml.parsers.XMLParser',\n ),\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework_xml.renderers.XMLRenderer',\n ),\n}",
"title": "Installation & configuration"
- },
+ },
{
- "location": "/api-guide/parsers/#messagepack",
- "text": "MessagePack is a fast, efficient binary serialization format. Juan Riaza maintains the djangorestframework-msgpack package which provides MessagePack renderer and parser support for REST framework.",
+ "location": "/api-guide/parsers/#messagepack",
+ "text": "MessagePack is a fast, efficient binary serialization format. Juan Riaza maintains the djangorestframework-msgpack package which provides MessagePack renderer and parser support for REST framework.",
"title": "MessagePack"
- },
+ },
{
- "location": "/api-guide/parsers/#camelcase-json",
- "text": "djangorestframework-camel-case provides camel case JSON renderers and parsers for REST framework. This allows serializers to use Python-style underscored field names, but be exposed in the API as Javascript-style camel case field names. It is maintained by Vitaly Babiy .",
+ "location": "/api-guide/parsers/#camelcase-json",
+ "text": "djangorestframework-camel-case provides camel case JSON renderers and parsers for REST framework. This allows serializers to use Python-style underscored field names, but be exposed in the API as Javascript-style camel case field names. It is maintained by Vitaly Babiy .",
"title": "CamelCase JSON"
- },
+ },
{
- "location": "/api-guide/renderers/",
- "text": "Renderers\n\n\n\n\nBefore a TemplateResponse instance can be returned to the client, it must be rendered. The rendering process takes the intermediate representation of template and context, and turns it into the final byte stream that can be served to the client.\n\n\n\u2014 \nDjango documentation\n\n\n\n\nREST framework includes a number of built in Renderer classes, that allow you to return responses with various media types. There is also support for defining your own custom renderers, which gives you the flexibility to design your own media types.\n\n\nHow the renderer is determined\n\n\nThe set of valid renderers for a view is always defined as a list of classes. When a view is entered REST framework will perform content negotiation on the incoming request, and determine the most appropriate renderer to satisfy the request.\n\n\nThe basic process of content negotiation involves examining the request's \nAccept\n header, to determine which media types it expects in the response. Optionally, format suffixes on the URL may be used to explicitly request a particular representation. For example the URL \nhttp://example.com/api/users_count.json\n might be an endpoint that always returns JSON data.\n\n\nFor more information see the documentation on \ncontent negotiation\n.\n\n\nSetting the renderers\n\n\nThe default set of renderers may be set globally, using the \nDEFAULT_RENDERER_CLASSES\n setting. For example, the following settings would use \nJSON\n as the main media type and also include the self describing API.\n\n\nREST_FRAMEWORK = {\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework.renderers.JSONRenderer',\n 'rest_framework.renderers.BrowsableAPIRenderer',\n )\n}\n\n\n\nYou can also set the renderers used for an individual view, or viewset,\nusing the \nAPIView\n class-based views.\n\n\nfrom django.contrib.auth.models import User\nfrom rest_framework.renderers import JSONRenderer\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\n\nclass UserCountView(APIView):\n \"\"\"\n A view that returns the count of active users in JSON.\n \"\"\"\n renderer_classes = (JSONRenderer, )\n\n def get(self, request, format=None):\n user_count = User.objects.filter(active=True).count()\n content = {'user_count': user_count}\n return Response(content)\n\n\n\nOr, if you're using the \n@api_view\n decorator with function based views.\n\n\n@api_view(['GET'])\n@renderer_classes((JSONRenderer,))\ndef user_count_view(request, format=None):\n \"\"\"\n A view that returns the count of active users in JSON.\n \"\"\"\n user_count = User.objects.filter(active=True).count()\n content = {'user_count': user_count}\n return Response(content)\n\n\n\nOrdering of renderer classes\n\n\nIt's important when specifying the renderer classes for your API to think about what priority you want to assign to each media type. If a client underspecifies the representations it can accept, such as sending an \nAccept: */*\n header, or not including an \nAccept\n header at all, then REST framework will select the first renderer in the list to use for the response.\n\n\nFor example if your API serves JSON responses and the HTML browsable API, you might want to make \nJSONRenderer\n your default renderer, in order to send \nJSON\n responses to clients that do not specify an \nAccept\n header.\n\n\nIf your API includes views that can serve both regular webpages and API responses depending on the request, then you might consider making \nTemplateHTMLRenderer\n your default renderer, in order to play nicely with older browsers that send \nbroken accept headers\n.\n\n\n\n\nAPI Reference\n\n\nJSONRenderer\n\n\nRenders the request data into \nJSON\n, using utf-8 encoding.\n\n\nNote that the default style is to include unicode characters, and render the response using a compact style with no unnecessary whitespace:\n\n\n{\"unicode black star\":\"\u2605\",\"value\":999}\n\n\n\nThe client may additionally include an \n'indent'\n media type parameter, in which case the returned \nJSON\n will be indented. For example \nAccept: application/json; indent=4\n.\n\n\n{\n \"unicode black star\": \"\u2605\",\n \"value\": 999\n}\n\n\n\nThe default JSON encoding style can be altered using the \nUNICODE_JSON\n and \nCOMPACT_JSON\n settings keys.\n\n\n.media_type\n: \napplication/json\n\n\n.format\n: \n'json'\n\n\n.charset\n: \nNone\n\n\nTemplateHTMLRenderer\n\n\nRenders data to HTML, using Django's standard template rendering.\nUnlike other renderers, the data passed to the \nResponse\n does not need to be serialized. Also, unlike other renderers, you may want to include a \ntemplate_name\n argument when creating the \nResponse\n.\n\n\nThe TemplateHTMLRenderer will create a \nRequestContext\n, using the \nresponse.data\n as the context dict, and determine a template name to use to render the context.\n\n\nThe template name is determined by (in order of preference):\n\n\n\n\nAn explicit \ntemplate_name\n argument passed to the response.\n\n\nAn explicit \n.template_name\n attribute set on this class.\n\n\nThe return result of calling \nview.get_template_names()\n.\n\n\n\n\nAn example of a view that uses \nTemplateHTMLRenderer\n:\n\n\nclass UserDetail(generics.RetrieveAPIView):\n \"\"\"\n A view that returns a templated HTML representation of a given user.\n \"\"\"\n queryset = User.objects.all()\n renderer_classes = (TemplateHTMLRenderer,)\n\n def get(self, request, *args, **kwargs):\n self.object = self.get_object()\n return Response({'user': self.object}, template_name='user_detail.html')\n\n\n\nYou can use \nTemplateHTMLRenderer\n either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint.\n\n\nIf you're building websites that use \nTemplateHTMLRenderer\n along with other renderer classes, you should consider listing \nTemplateHTMLRenderer\n as the first class in the \nrenderer_classes\n list, so that it will be prioritised first even for browsers that send poorly formed \nACCEPT:\n headers.\n\n\nSee the \nHTML & Forms\n Topic Page\n for further examples of \nTemplateHTMLRenderer\n usage.\n\n\n.media_type\n: \ntext/html\n\n\n.format\n: \n'html'\n\n\n.charset\n: \nutf-8\n\n\nSee also: \nStaticHTMLRenderer\n\n\nStaticHTMLRenderer\n\n\nA simple renderer that simply returns pre-rendered HTML. Unlike other renderers, the data passed to the response object should be a string representing the content to be returned.\n\n\nAn example of a view that uses \nStaticHTMLRenderer\n:\n\n\n@api_view(('GET',))\n@renderer_classes((StaticHTMLRenderer,))\ndef simple_html_view(request):\n data = 'Hello, world
'\n return Response(data)\n\n\n\nYou can use \nStaticHTMLRenderer\n either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint.\n\n\n.media_type\n: \ntext/html\n\n\n.format\n: \n'html'\n\n\n.charset\n: \nutf-8\n\n\nSee also: \nTemplateHTMLRenderer\n\n\nBrowsableAPIRenderer\n\n\nRenders data into HTML for the Browsable API:\n\n\n\n\nThis renderer will determine which other renderer would have been given highest priority, and use that to display an API style response within the HTML page.\n\n\n.media_type\n: \ntext/html\n\n\n.format\n: \n'api'\n\n\n.charset\n: \nutf-8\n\n\n.template\n: \n'rest_framework/api.html'\n\n\nCustomizing BrowsableAPIRenderer\n\n\nBy default the response content will be rendered with the highest priority renderer apart from \nBrowsableAPIRenderer\n. If you need to customize this behavior, for example to use HTML as the default return format, but use JSON in the browsable API, you can do so by overriding the \nget_default_renderer()\n method. For example:\n\n\nclass CustomBrowsableAPIRenderer(BrowsableAPIRenderer):\n def get_default_renderer(self, view):\n return JSONRenderer()\n\n\n\nAdminRenderer\n\n\nRenders data into HTML for an admin-like display:\n\n\n\n\nThis renderer is suitable for CRUD-style web APIs that should also present a user-friendly interface for managing the data.\n\n\nNote that views that have nested or list serializers for their input won't work well with the \nAdminRenderer\n, as the HTML forms are unable to properly support them.\n\n\nNote\n: The \nAdminRenderer\n is only able to include links to detail pages when a properly configured \nURL_FIELD_NAME\n (\nurl\n by default) attribute is present in the data. For \nHyperlinkedModelSerializer\n this will be the case, but for \nModelSerializer\n or plain \nSerializer\n classes you'll need to make sure to include the field explicitly. For example here we use models \nget_absolute_url\n method:\n\n\nclass AccountSerializer(serializers.ModelSerializer):\n url = serializers.CharField(source='get_absolute_url', read_only=True)\n\n class Meta:\n model = Account\n\n\n\n.media_type\n: \ntext/html\n\n\n.format\n: \n'admin'\n\n\n.charset\n: \nutf-8\n\n\n.template\n: \n'rest_framework/admin.html'\n\n\nHTMLFormRenderer\n\n\nRenders data returned by a serializer into an HTML form. The output of this renderer does not include the enclosing \n\n\n\n\nFor more information see the \nHTML & Forms\n documentation.\n\n\n.media_type\n: \ntext/html\n\n\n.format\n: \n'form'\n\n\n.charset\n: \nutf-8\n\n\n.template\n: \n'rest_framework/horizontal/form.html'\n\n\nMultiPartRenderer\n\n\nThis renderer is used for rendering HTML multipart form data. \nIt is not suitable as a response renderer\n, but is instead used for creating test requests, using REST framework's \ntest client and test request factory\n.\n\n\n.media_type\n: \nmultipart/form-data; boundary=BoUnDaRyStRiNg\n\n\n.format\n: \n'multipart'\n\n\n.charset\n: \nutf-8\n\n\n\n\nCustom renderers\n\n\nTo implement a custom renderer, you should override \nBaseRenderer\n, set the \n.media_type\n and \n.format\n properties, and implement the \n.render(self, data, media_type=None, renderer_context=None)\n method.\n\n\nThe method should return a bytestring, which will be used as the body of the HTTP response.\n\n\nThe arguments passed to the \n.render()\n method are:\n\n\ndata\n\n\nThe request data, as set by the \nResponse()\n instantiation.\n\n\nmedia_type=None\n\n\nOptional. If provided, this is the accepted media type, as determined by the content negotiation stage.\n\n\nDepending on the client's \nAccept:\n header, this may be more specific than the renderer's \nmedia_type\n attribute, and may include media type parameters. For example \n\"application/json; nested=true\"\n.\n\n\nrenderer_context=None\n\n\nOptional. If provided, this is a dictionary of contextual information provided by the view.\n\n\nBy default this will include the following keys: \nview\n, \nrequest\n, \nresponse\n, \nargs\n, \nkwargs\n.\n\n\nExample\n\n\nThe following is an example plaintext renderer that will return a response with the \ndata\n parameter as the content of the response.\n\n\nfrom django.utils.encoding import smart_unicode\nfrom rest_framework import renderers\n\n\nclass PlainTextRenderer(renderers.BaseRenderer):\n media_type = 'text/plain'\n format = 'txt'\n\n def render(self, data, media_type=None, renderer_context=None):\n return data.encode(self.charset)\n\n\n\nSetting the character set\n\n\nBy default renderer classes are assumed to be using the \nUTF-8\n encoding. To use a different encoding, set the \ncharset\n attribute on the renderer.\n\n\nclass PlainTextRenderer(renderers.BaseRenderer):\n media_type = 'text/plain'\n format = 'txt'\n charset = 'iso-8859-1'\n\n def render(self, data, media_type=None, renderer_context=None):\n return data.encode(self.charset)\n\n\n\nNote that if a renderer class returns a unicode string, then the response content will be coerced into a bytestring by the \nResponse\n class, with the \ncharset\n attribute set on the renderer used to determine the encoding.\n\n\nIf the renderer returns a bytestring representing raw binary content, you should set a charset value of \nNone\n, which will ensure the \nContent-Type\n header of the response will not have a \ncharset\n value set.\n\n\nIn some cases you may also want to set the \nrender_style\n attribute to \n'binary'\n. Doing so will also ensure that the browsable API will not attempt to display the binary content as a string.\n\n\nclass JPEGRenderer(renderers.BaseRenderer):\n media_type = 'image/jpeg'\n format = 'jpg'\n charset = None\n render_style = 'binary'\n\n def render(self, data, media_type=None, renderer_context=None):\n return data\n\n\n\n\n\nAdvanced renderer usage\n\n\nYou can do some pretty flexible things using REST framework's renderers. Some examples...\n\n\n\n\nProvide either flat or nested representations from the same endpoint, depending on the requested media type.\n\n\nServe both regular HTML webpages, and JSON based API responses from the same endpoints.\n\n\nSpecify multiple types of HTML representation for API clients to use.\n\n\nUnderspecify a renderer's media type, such as using \nmedia_type = 'image/*'\n, and use the \nAccept\n header to vary the encoding of the response.\n\n\n\n\nVarying behaviour by media type\n\n\nIn some cases you might want your view to use different serialization styles depending on the accepted media type. If you need to do this you can access \nrequest.accepted_renderer\n to determine the negotiated renderer that will be used for the response.\n\n\nFor example:\n\n\n@api_view(('GET',))\n@renderer_classes((TemplateHTMLRenderer, JSONRenderer))\ndef list_users(request):\n \"\"\"\n A view that can return JSON or HTML representations\n of the users in the system.\n \"\"\"\n queryset = Users.objects.filter(active=True)\n\n if request.accepted_renderer.format == 'html':\n # TemplateHTMLRenderer takes a context dict,\n # and additionally requires a 'template_name'.\n # It does not require serialization.\n data = {'users': queryset}\n return Response(data, template_name='list_users.html')\n\n # JSONRenderer requires serialized data as normal.\n serializer = UserSerializer(instance=queryset)\n data = serializer.data\n return Response(data)\n\n\n\nUnderspecifying the media type\n\n\nIn some cases you might want a renderer to serve a range of media types.\nIn this case you can underspecify the media types it should respond to, by using a \nmedia_type\n value such as \nimage/*\n, or \n*/*\n.\n\n\nIf you underspecify the renderer's media type, you should make sure to specify the media type explicitly when you return the response, using the \ncontent_type\n attribute. For example:\n\n\nreturn Response(data, content_type='image/png')\n\n\n\nDesigning your media types\n\n\nFor the purposes of many Web APIs, simple \nJSON\n responses with hyperlinked relations may be sufficient. If you want to fully embrace RESTful design and \nHATEOAS\n you'll need to consider the design and usage of your media types in more detail.\n\n\nIn \nthe words of Roy Fielding\n, \"A REST API should spend almost all of its descriptive effort in defining the media type(s) used for representing resources and driving application state, or in defining extended relation names and/or hypertext-enabled mark-up for existing standard media types.\".\n\n\nFor good examples of custom media types, see GitHub's use of a custom \napplication/vnd.github+json\n media type, and Mike Amundsen's IANA approved \napplication/vnd.collection+json\n JSON-based hypermedia.\n\n\nHTML error views\n\n\nTypically a renderer will behave the same regardless of if it's dealing with a regular response, or with a response caused by an exception being raised, such as an \nHttp404\n or \nPermissionDenied\n exception, or a subclass of \nAPIException\n.\n\n\nIf you're using either the \nTemplateHTMLRenderer\n or the \nStaticHTMLRenderer\n and an exception is raised, the behavior is slightly different, and mirrors \nDjango's default handling of error views\n.\n\n\nExceptions raised and handled by an HTML renderer will attempt to render using one of the following methods, by order of precedence.\n\n\n\n\nLoad and render a template named \n{status_code}.html\n.\n\n\nLoad and render a template named \napi_exception.html\n.\n\n\nRender the HTTP status code and text, for example \"404 Not Found\".\n\n\n\n\nTemplates will render with a \nRequestContext\n which includes the \nstatus_code\n and \ndetails\n keys.\n\n\nNote\n: If \nDEBUG=True\n, Django's standard traceback error page will be displayed instead of rendering the HTTP status code and text.\n\n\n\n\nThird party packages\n\n\nThe following third party packages are also available.\n\n\nYAML\n\n\nREST framework YAML\n provides \nYAML\n parsing and rendering support. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.\n\n\nInstallation & configuration\n\n\nInstall using pip.\n\n\n$ pip install djangorestframework-yaml\n\n\n\nModify your REST framework settings.\n\n\nREST_FRAMEWORK = {\n 'DEFAULT_PARSER_CLASSES': (\n 'rest_framework_yaml.parsers.YAMLParser',\n ),\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework_yaml.renderers.YAMLRenderer',\n ),\n}\n\n\n\nXML\n\n\nREST Framework XML\n provides a simple informal XML format. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.\n\n\nInstallation & configuration\n\n\nInstall using pip.\n\n\n$ pip install djangorestframework-xml\n\n\n\nModify your REST framework settings.\n\n\nREST_FRAMEWORK = {\n 'DEFAULT_PARSER_CLASSES': (\n 'rest_framework_xml.parsers.XMLParser',\n ),\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework_xml.renderers.XMLRenderer',\n ),\n}\n\n\n\nJSONP\n\n\nREST framework JSONP\n provides JSONP rendering support. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.\n\n\n\n\nWarning\n: If you require cross-domain AJAX requests, you should generally be using the more modern approach of \nCORS\n as an alternative to \nJSONP\n. See the \nCORS documentation\n for more details.\n\n\nThe \njsonp\n approach is essentially a browser hack, and is \nonly appropriate for globally readable API endpoints\n, where \nGET\n requests are unauthenticated and do not require any user permissions.\n\n\n\n\nInstallation & configuration\n\n\nInstall using pip.\n\n\n$ pip install djangorestframework-jsonp\n\n\n\nModify your REST framework settings.\n\n\nREST_FRAMEWORK = {\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework_jsonp.renderers.JSONPRenderer',\n ),\n}\n\n\n\nMessagePack\n\n\nMessagePack\n is a fast, efficient binary serialization format. \nJuan Riaza\n maintains the \ndjangorestframework-msgpack\n package which provides MessagePack renderer and parser support for REST framework.\n\n\nXLSX (Binary Spreadsheet Endpoints)\n\n\nXLSX is the world's most popular binary spreadsheet format. \nTim Allen\n of \nThe Wharton School\n maintains \ndrf-renderer-xlsx\n, which renders an endpoint as an XLSX spreadsheet using OpenPyXL, and allows the client to download it. Spreadsheets can be styled on a per-view basis.\n\n\nInstallation & configuration\n\n\nInstall using pip.\n\n\n$ pip install drf-renderer-xlsx\n\n\n\nModify your REST framework settings.\n\n\nREST_FRAMEWORK = {\n ...\n\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework.renderers.JSONRenderer',\n 'rest_framework.renderers.BrowsableAPIRenderer',\n 'drf_renderer_xlsx.renderers.XLSXRenderer',\n ),\n}\n\n\n\nTo avoid having a file streamed without a filename (which the browser will often default to the filename \"download\", with no extension), we need to use a mixin to override the \nContent-Disposition\n header. If no filename is provided, it will default to \nexport.xlsx\n. For example:\n\n\nfrom rest_framework.viewsets import ReadOnlyModelViewSet\nfrom drf_renderer_xlsx.mixins import XLSXFileMixin\nfrom drf_renderer_xlsx.renderers import XLSXRenderer\n\nfrom .models import MyExampleModel\nfrom .serializers import MyExampleSerializer\n\nclass MyExampleViewSet(XLSXFileMixin, ReadOnlyModelViewSet):\n queryset = MyExampleModel.objects.all()\n serializer_class = MyExampleSerializer\n renderer_classes = (XLSXRenderer,)\n filename = 'my_export.xlsx'\n\n\n\nCSV\n\n\nComma-separated values are a plain-text tabular data format, that can be easily imported into spreadsheet applications. \nMjumbe Poe\n maintains the \ndjangorestframework-csv\n package which provides CSV renderer support for REST framework.\n\n\nUltraJSON\n\n\nUltraJSON\n is an optimized C JSON encoder which can give significantly faster JSON rendering. \nJacob Haslehurst\n maintains the \ndrf-ujson-renderer\n package which implements JSON rendering using the UJSON package.\n\n\nCamelCase JSON\n\n\ndjangorestframework-camel-case\n provides camel case JSON renderers and parsers for REST framework. This allows serializers to use Python-style underscored field names, but be exposed in the API as Javascript-style camel case field names. It is maintained by \nVitaly Babiy\n.\n\n\nPandas (CSV, Excel, PNG)\n\n\nDjango REST Pandas\n provides a serializer and renderers that support additional data processing and output via the \nPandas\n DataFrame API. Django REST Pandas includes renderers for Pandas-style CSV files, Excel workbooks (both \n.xls\n and \n.xlsx\n), and a number of \nother formats\n. It is maintained by \nS. Andrew Sheppard\n as part of the \nwq Project\n.\n\n\nLaTeX\n\n\nRest Framework Latex\n provides a renderer that outputs PDFs using Laulatex. It is maintained by \nPebble (S/F Software)\n.",
+ "location": "/api-guide/renderers/",
+ "text": "Renderers\n\n\n\n\nBefore a TemplateResponse instance can be returned to the client, it must be rendered. The rendering process takes the intermediate representation of template and context, and turns it into the final byte stream that can be served to the client.\n\n\n \nDjango documentation\n\n\n\n\nREST framework includes a number of built in Renderer classes, that allow you to return responses with various media types. There is also support for defining your own custom renderers, which gives you the flexibility to design your own media types.\n\n\nHow the renderer is determined\n\n\nThe set of valid renderers for a view is always defined as a list of classes. When a view is entered REST framework will perform content negotiation on the incoming request, and determine the most appropriate renderer to satisfy the request.\n\n\nThe basic process of content negotiation involves examining the request's \nAccept\n header, to determine which media types it expects in the response. Optionally, format suffixes on the URL may be used to explicitly request a particular representation. For example the URL \nhttp://example.com/api/users_count.json\n might be an endpoint that always returns JSON data.\n\n\nFor more information see the documentation on \ncontent negotiation\n.\n\n\nSetting the renderers\n\n\nThe default set of renderers may be set globally, using the \nDEFAULT_RENDERER_CLASSES\n setting. For example, the following settings would use \nJSON\n as the main media type and also include the self describing API.\n\n\nREST_FRAMEWORK = {\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework.renderers.JSONRenderer',\n 'rest_framework.renderers.BrowsableAPIRenderer',\n )\n}\n\n\n\nYou can also set the renderers used for an individual view, or viewset,\nusing the \nAPIView\n class-based views.\n\n\nfrom django.contrib.auth.models import User\nfrom rest_framework.renderers import JSONRenderer\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\n\nclass UserCountView(APIView):\n \"\"\"\n A view that returns the count of active users in JSON.\n \"\"\"\n renderer_classes = (JSONRenderer, )\n\n def get(self, request, format=None):\n user_count = User.objects.filter(active=True).count()\n content = {'user_count': user_count}\n return Response(content)\n\n\n\nOr, if you're using the \n@api_view\n decorator with function based views.\n\n\n@api_view(['GET'])\n@renderer_classes((JSONRenderer,))\ndef user_count_view(request, format=None):\n \"\"\"\n A view that returns the count of active users in JSON.\n \"\"\"\n user_count = User.objects.filter(active=True).count()\n content = {'user_count': user_count}\n return Response(content)\n\n\n\nOrdering of renderer classes\n\n\nIt's important when specifying the renderer classes for your API to think about what priority you want to assign to each media type. If a client underspecifies the representations it can accept, such as sending an \nAccept: */*\n header, or not including an \nAccept\n header at all, then REST framework will select the first renderer in the list to use for the response.\n\n\nFor example if your API serves JSON responses and the HTML browsable API, you might want to make \nJSONRenderer\n your default renderer, in order to send \nJSON\n responses to clients that do not specify an \nAccept\n header.\n\n\nIf your API includes views that can serve both regular webpages and API responses depending on the request, then you might consider making \nTemplateHTMLRenderer\n your default renderer, in order to play nicely with older browsers that send \nbroken accept headers\n.\n\n\n\n\nAPI Reference\n\n\nJSONRenderer\n\n\nRenders the request data into \nJSON\n, using utf-8 encoding.\n\n\nNote that the default style is to include unicode characters, and render the response using a compact style with no unnecessary whitespace:\n\n\n{\"unicode black star\":\"\u2605\",\"value\":999}\n\n\n\nThe client may additionally include an \n'indent'\n media type parameter, in which case the returned \nJSON\n will be indented. For example \nAccept: application/json; indent=4\n.\n\n\n{\n \"unicode black star\": \"\u2605\",\n \"value\": 999\n}\n\n\n\nThe default JSON encoding style can be altered using the \nUNICODE_JSON\n and \nCOMPACT_JSON\n settings keys.\n\n\n.media_type\n: \napplication/json\n\n\n.format\n: \n'json'\n\n\n.charset\n: \nNone\n\n\nTemplateHTMLRenderer\n\n\nRenders data to HTML, using Django's standard template rendering.\nUnlike other renderers, the data passed to the \nResponse\n does not need to be serialized. Also, unlike other renderers, you may want to include a \ntemplate_name\n argument when creating the \nResponse\n.\n\n\nThe TemplateHTMLRenderer will create a \nRequestContext\n, using the \nresponse.data\n as the context dict, and determine a template name to use to render the context.\n\n\nThe template name is determined by (in order of preference):\n\n\n\n\nAn explicit \ntemplate_name\n argument passed to the response.\n\n\nAn explicit \n.template_name\n attribute set on this class.\n\n\nThe return result of calling \nview.get_template_names()\n.\n\n\n\n\nAn example of a view that uses \nTemplateHTMLRenderer\n:\n\n\nclass UserDetail(generics.RetrieveAPIView):\n \"\"\"\n A view that returns a templated HTML representation of a given user.\n \"\"\"\n queryset = User.objects.all()\n renderer_classes = (TemplateHTMLRenderer,)\n\n def get(self, request, *args, **kwargs):\n self.object = self.get_object()\n return Response({'user': self.object}, template_name='user_detail.html')\n\n\n\nYou can use \nTemplateHTMLRenderer\n either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint.\n\n\nIf you're building websites that use \nTemplateHTMLRenderer\n along with other renderer classes, you should consider listing \nTemplateHTMLRenderer\n as the first class in the \nrenderer_classes\n list, so that it will be prioritised first even for browsers that send poorly formed \nACCEPT:\n headers.\n\n\nSee the \nHTML \n Forms\n Topic Page\n for further examples of \nTemplateHTMLRenderer\n usage.\n\n\n.media_type\n: \ntext/html\n\n\n.format\n: \n'html'\n\n\n.charset\n: \nutf-8\n\n\nSee also: \nStaticHTMLRenderer\n\n\nStaticHTMLRenderer\n\n\nA simple renderer that simply returns pre-rendered HTML. Unlike other renderers, the data passed to the response object should be a string representing the content to be returned.\n\n\nAn example of a view that uses \nStaticHTMLRenderer\n:\n\n\n@api_view(('GET',))\n@renderer_classes((StaticHTMLRenderer,))\ndef simple_html_view(request):\n data = '\nhtml\nbody\nh1\nHello, world\n/h1\n/body\n/html\n'\n return Response(data)\n\n\n\nYou can use \nStaticHTMLRenderer\n either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint.\n\n\n.media_type\n: \ntext/html\n\n\n.format\n: \n'html'\n\n\n.charset\n: \nutf-8\n\n\nSee also: \nTemplateHTMLRenderer\n\n\nBrowsableAPIRenderer\n\n\nRenders data into HTML for the Browsable API:\n\n\n\n\nThis renderer will determine which other renderer would have been given highest priority, and use that to display an API style response within the HTML page.\n\n\n.media_type\n: \ntext/html\n\n\n.format\n: \n'api'\n\n\n.charset\n: \nutf-8\n\n\n.template\n: \n'rest_framework/api.html'\n\n\nCustomizing BrowsableAPIRenderer\n\n\nBy default the response content will be rendered with the highest priority renderer apart from \nBrowsableAPIRenderer\n. If you need to customize this behavior, for example to use HTML as the default return format, but use JSON in the browsable API, you can do so by overriding the \nget_default_renderer()\n method. For example:\n\n\nclass CustomBrowsableAPIRenderer(BrowsableAPIRenderer):\n def get_default_renderer(self, view):\n return JSONRenderer()\n\n\n\nAdminRenderer\n\n\nRenders data into HTML for an admin-like display:\n\n\n\n\nThis renderer is suitable for CRUD-style web APIs that should also present a user-friendly interface for managing the data.\n\n\nNote that views that have nested or list serializers for their input won't work well with the \nAdminRenderer\n, as the HTML forms are unable to properly support them.\n\n\nNote\n: The \nAdminRenderer\n is only able to include links to detail pages when a properly configured \nURL_FIELD_NAME\n (\nurl\n by default) attribute is present in the data. For \nHyperlinkedModelSerializer\n this will be the case, but for \nModelSerializer\n or plain \nSerializer\n classes you'll need to make sure to include the field explicitly. For example here we use models \nget_absolute_url\n method:\n\n\nclass AccountSerializer(serializers.ModelSerializer):\n url = serializers.CharField(source='get_absolute_url', read_only=True)\n\n class Meta:\n model = Account\n\n\n\n.media_type\n: \ntext/html\n\n\n.format\n: \n'admin'\n\n\n.charset\n: \nutf-8\n\n\n.template\n: \n'rest_framework/admin.html'\n\n\nHTMLFormRenderer\n\n\nRenders data returned by a serializer into an HTML form. The output of this renderer does not include the enclosing \nform\n tags, a hidden CSRF input or any submit buttons.\n\n\nThis renderer is not intended to be used directly, but can instead be used in templates by passing a serializer instance to the \nrender_form\n template tag.\n\n\n{% load rest_framework %}\n\n\nform action=\"/submit-report/\" method=\"post\"\n\n {% csrf_token %}\n {% render_form serializer %}\n \ninput type=\"submit\" value=\"Save\" /\n\n\n/form\n\n\n\n\nFor more information see the \nHTML \n Forms\n documentation.\n\n\n.media_type\n: \ntext/html\n\n\n.format\n: \n'form'\n\n\n.charset\n: \nutf-8\n\n\n.template\n: \n'rest_framework/horizontal/form.html'\n\n\nMultiPartRenderer\n\n\nThis renderer is used for rendering HTML multipart form data. \nIt is not suitable as a response renderer\n, but is instead used for creating test requests, using REST framework's \ntest client and test request factory\n.\n\n\n.media_type\n: \nmultipart/form-data; boundary=BoUnDaRyStRiNg\n\n\n.format\n: \n'multipart'\n\n\n.charset\n: \nutf-8\n\n\n\n\nCustom renderers\n\n\nTo implement a custom renderer, you should override \nBaseRenderer\n, set the \n.media_type\n and \n.format\n properties, and implement the \n.render(self, data, media_type=None, renderer_context=None)\n method.\n\n\nThe method should return a bytestring, which will be used as the body of the HTTP response.\n\n\nThe arguments passed to the \n.render()\n method are:\n\n\ndata\n\n\nThe request data, as set by the \nResponse()\n instantiation.\n\n\nmedia_type=None\n\n\nOptional. If provided, this is the accepted media type, as determined by the content negotiation stage.\n\n\nDepending on the client's \nAccept:\n header, this may be more specific than the renderer's \nmedia_type\n attribute, and may include media type parameters. For example \n\"application/json; nested=true\"\n.\n\n\nrenderer_context=None\n\n\nOptional. If provided, this is a dictionary of contextual information provided by the view.\n\n\nBy default this will include the following keys: \nview\n, \nrequest\n, \nresponse\n, \nargs\n, \nkwargs\n.\n\n\nExample\n\n\nThe following is an example plaintext renderer that will return a response with the \ndata\n parameter as the content of the response.\n\n\nfrom django.utils.encoding import smart_unicode\nfrom rest_framework import renderers\n\n\nclass PlainTextRenderer(renderers.BaseRenderer):\n media_type = 'text/plain'\n format = 'txt'\n\n def render(self, data, media_type=None, renderer_context=None):\n return data.encode(self.charset)\n\n\n\nSetting the character set\n\n\nBy default renderer classes are assumed to be using the \nUTF-8\n encoding. To use a different encoding, set the \ncharset\n attribute on the renderer.\n\n\nclass PlainTextRenderer(renderers.BaseRenderer):\n media_type = 'text/plain'\n format = 'txt'\n charset = 'iso-8859-1'\n\n def render(self, data, media_type=None, renderer_context=None):\n return data.encode(self.charset)\n\n\n\nNote that if a renderer class returns a unicode string, then the response content will be coerced into a bytestring by the \nResponse\n class, with the \ncharset\n attribute set on the renderer used to determine the encoding.\n\n\nIf the renderer returns a bytestring representing raw binary content, you should set a charset value of \nNone\n, which will ensure the \nContent-Type\n header of the response will not have a \ncharset\n value set.\n\n\nIn some cases you may also want to set the \nrender_style\n attribute to \n'binary'\n. Doing so will also ensure that the browsable API will not attempt to display the binary content as a string.\n\n\nclass JPEGRenderer(renderers.BaseRenderer):\n media_type = 'image/jpeg'\n format = 'jpg'\n charset = None\n render_style = 'binary'\n\n def render(self, data, media_type=None, renderer_context=None):\n return data\n\n\n\n\n\nAdvanced renderer usage\n\n\nYou can do some pretty flexible things using REST framework's renderers. Some examples...\n\n\n\n\nProvide either flat or nested representations from the same endpoint, depending on the requested media type.\n\n\nServe both regular HTML webpages, and JSON based API responses from the same endpoints.\n\n\nSpecify multiple types of HTML representation for API clients to use.\n\n\nUnderspecify a renderer's media type, such as using \nmedia_type = 'image/*'\n, and use the \nAccept\n header to vary the encoding of the response.\n\n\n\n\nVarying behaviour by media type\n\n\nIn some cases you might want your view to use different serialization styles depending on the accepted media type. If you need to do this you can access \nrequest.accepted_renderer\n to determine the negotiated renderer that will be used for the response.\n\n\nFor example:\n\n\n@api_view(('GET',))\n@renderer_classes((TemplateHTMLRenderer, JSONRenderer))\ndef list_users(request):\n \"\"\"\n A view that can return JSON or HTML representations\n of the users in the system.\n \"\"\"\n queryset = Users.objects.filter(active=True)\n\n if request.accepted_renderer.format == 'html':\n # TemplateHTMLRenderer takes a context dict,\n # and additionally requires a 'template_name'.\n # It does not require serialization.\n data = {'users': queryset}\n return Response(data, template_name='list_users.html')\n\n # JSONRenderer requires serialized data as normal.\n serializer = UserSerializer(instance=queryset)\n data = serializer.data\n return Response(data)\n\n\n\nUnderspecifying the media type\n\n\nIn some cases you might want a renderer to serve a range of media types.\nIn this case you can underspecify the media types it should respond to, by using a \nmedia_type\n value such as \nimage/*\n, or \n*/*\n.\n\n\nIf you underspecify the renderer's media type, you should make sure to specify the media type explicitly when you return the response, using the \ncontent_type\n attribute. For example:\n\n\nreturn Response(data, content_type='image/png')\n\n\n\nDesigning your media types\n\n\nFor the purposes of many Web APIs, simple \nJSON\n responses with hyperlinked relations may be sufficient. If you want to fully embrace RESTful design and \nHATEOAS\n you'll need to consider the design and usage of your media types in more detail.\n\n\nIn \nthe words of Roy Fielding\n, \"A REST API should spend almost all of its descriptive effort in defining the media type(s) used for representing resources and driving application state, or in defining extended relation names and/or hypertext-enabled mark-up for existing standard media types.\".\n\n\nFor good examples of custom media types, see GitHub's use of a custom \napplication/vnd.github+json\n media type, and Mike Amundsen's IANA approved \napplication/vnd.collection+json\n JSON-based hypermedia.\n\n\nHTML error views\n\n\nTypically a renderer will behave the same regardless of if it's dealing with a regular response, or with a response caused by an exception being raised, such as an \nHttp404\n or \nPermissionDenied\n exception, or a subclass of \nAPIException\n.\n\n\nIf you're using either the \nTemplateHTMLRenderer\n or the \nStaticHTMLRenderer\n and an exception is raised, the behavior is slightly different, and mirrors \nDjango's default handling of error views\n.\n\n\nExceptions raised and handled by an HTML renderer will attempt to render using one of the following methods, by order of precedence.\n\n\n\n\nLoad and render a template named \n{status_code}.html\n.\n\n\nLoad and render a template named \napi_exception.html\n.\n\n\nRender the HTTP status code and text, for example \"404 Not Found\".\n\n\n\n\nTemplates will render with a \nRequestContext\n which includes the \nstatus_code\n and \ndetails\n keys.\n\n\nNote\n: If \nDEBUG=True\n, Django's standard traceback error page will be displayed instead of rendering the HTTP status code and text.\n\n\n\n\nThird party packages\n\n\nThe following third party packages are also available.\n\n\nYAML\n\n\nREST framework YAML\n provides \nYAML\n parsing and rendering support. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.\n\n\nInstallation \n configuration\n\n\nInstall using pip.\n\n\n$ pip install djangorestframework-yaml\n\n\n\nModify your REST framework settings.\n\n\nREST_FRAMEWORK = {\n 'DEFAULT_PARSER_CLASSES': (\n 'rest_framework_yaml.parsers.YAMLParser',\n ),\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework_yaml.renderers.YAMLRenderer',\n ),\n}\n\n\n\nXML\n\n\nREST Framework XML\n provides a simple informal XML format. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.\n\n\nInstallation \n configuration\n\n\nInstall using pip.\n\n\n$ pip install djangorestframework-xml\n\n\n\nModify your REST framework settings.\n\n\nREST_FRAMEWORK = {\n 'DEFAULT_PARSER_CLASSES': (\n 'rest_framework_xml.parsers.XMLParser',\n ),\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework_xml.renderers.XMLRenderer',\n ),\n}\n\n\n\nJSONP\n\n\nREST framework JSONP\n provides JSONP rendering support. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.\n\n\n\n\nWarning\n: If you require cross-domain AJAX requests, you should generally be using the more modern approach of \nCORS\n as an alternative to \nJSONP\n. See the \nCORS documentation\n for more details.\n\n\nThe \njsonp\n approach is essentially a browser hack, and is \nonly appropriate for globally readable API endpoints\n, where \nGET\n requests are unauthenticated and do not require any user permissions.\n\n\n\n\nInstallation \n configuration\n\n\nInstall using pip.\n\n\n$ pip install djangorestframework-jsonp\n\n\n\nModify your REST framework settings.\n\n\nREST_FRAMEWORK = {\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework_jsonp.renderers.JSONPRenderer',\n ),\n}\n\n\n\nMessagePack\n\n\nMessagePack\n is a fast, efficient binary serialization format. \nJuan Riaza\n maintains the \ndjangorestframework-msgpack\n package which provides MessagePack renderer and parser support for REST framework.\n\n\nXLSX (Binary Spreadsheet Endpoints)\n\n\nXLSX is the world's most popular binary spreadsheet format. \nTim Allen\n of \nThe Wharton School\n maintains \ndrf-renderer-xlsx\n, which renders an endpoint as an XLSX spreadsheet using OpenPyXL, and allows the client to download it. Spreadsheets can be styled on a per-view basis.\n\n\nInstallation \n configuration\n\n\nInstall using pip.\n\n\n$ pip install drf-renderer-xlsx\n\n\n\nModify your REST framework settings.\n\n\nREST_FRAMEWORK = {\n ...\n\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework.renderers.JSONRenderer',\n 'rest_framework.renderers.BrowsableAPIRenderer',\n 'drf_renderer_xlsx.renderers.XLSXRenderer',\n ),\n}\n\n\n\nTo avoid having a file streamed without a filename (which the browser will often default to the filename \"download\", with no extension), we need to use a mixin to override the \nContent-Disposition\n header. If no filename is provided, it will default to \nexport.xlsx\n. For example:\n\n\nfrom rest_framework.viewsets import ReadOnlyModelViewSet\nfrom drf_renderer_xlsx.mixins import XLSXFileMixin\nfrom drf_renderer_xlsx.renderers import XLSXRenderer\n\nfrom .models import MyExampleModel\nfrom .serializers import MyExampleSerializer\n\nclass MyExampleViewSet(XLSXFileMixin, ReadOnlyModelViewSet):\n queryset = MyExampleModel.objects.all()\n serializer_class = MyExampleSerializer\n renderer_classes = (XLSXRenderer,)\n filename = 'my_export.xlsx'\n\n\n\nCSV\n\n\nComma-separated values are a plain-text tabular data format, that can be easily imported into spreadsheet applications. \nMjumbe Poe\n maintains the \ndjangorestframework-csv\n package which provides CSV renderer support for REST framework.\n\n\nUltraJSON\n\n\nUltraJSON\n is an optimized C JSON encoder which can give significantly faster JSON rendering. \nJacob Haslehurst\n maintains the \ndrf-ujson-renderer\n package which implements JSON rendering using the UJSON package.\n\n\nCamelCase JSON\n\n\ndjangorestframework-camel-case\n provides camel case JSON renderers and parsers for REST framework. This allows serializers to use Python-style underscored field names, but be exposed in the API as Javascript-style camel case field names. It is maintained by \nVitaly Babiy\n.\n\n\nPandas (CSV, Excel, PNG)\n\n\nDjango REST Pandas\n provides a serializer and renderers that support additional data processing and output via the \nPandas\n DataFrame API. Django REST Pandas includes renderers for Pandas-style CSV files, Excel workbooks (both \n.xls\n and \n.xlsx\n), and a number of \nother formats\n. It is maintained by \nS. Andrew Sheppard\n as part of the \nwq Project\n.\n\n\nLaTeX\n\n\nRest Framework Latex\n provides a renderer that outputs PDFs using Laulatex. It is maintained by \nPebble (S/F Software)\n.",
"title": "Renderers"
- },
+ },
{
- "location": "/api-guide/renderers/#renderers",
- "text": "Before a TemplateResponse instance can be returned to the client, it must be rendered. The rendering process takes the intermediate representation of template and context, and turns it into the final byte stream that can be served to the client. \u2014 Django documentation REST framework includes a number of built in Renderer classes, that allow you to return responses with various media types. There is also support for defining your own custom renderers, which gives you the flexibility to design your own media types.",
+ "location": "/api-guide/renderers/#renderers",
+ "text": "Before a TemplateResponse instance can be returned to the client, it must be rendered. The rendering process takes the intermediate representation of template and context, and turns it into the final byte stream that can be served to the client. Django documentation REST framework includes a number of built in Renderer classes, that allow you to return responses with various media types. There is also support for defining your own custom renderers, which gives you the flexibility to design your own media types.",
"title": "Renderers"
- },
+ },
{
- "location": "/api-guide/renderers/#how-the-renderer-is-determined",
- "text": "The set of valid renderers for a view is always defined as a list of classes. When a view is entered REST framework will perform content negotiation on the incoming request, and determine the most appropriate renderer to satisfy the request. The basic process of content negotiation involves examining the request's Accept header, to determine which media types it expects in the response. Optionally, format suffixes on the URL may be used to explicitly request a particular representation. For example the URL http://example.com/api/users_count.json might be an endpoint that always returns JSON data. For more information see the documentation on content negotiation .",
+ "location": "/api-guide/renderers/#how-the-renderer-is-determined",
+ "text": "The set of valid renderers for a view is always defined as a list of classes. When a view is entered REST framework will perform content negotiation on the incoming request, and determine the most appropriate renderer to satisfy the request. The basic process of content negotiation involves examining the request's Accept header, to determine which media types it expects in the response. Optionally, format suffixes on the URL may be used to explicitly request a particular representation. For example the URL http://example.com/api/users_count.json might be an endpoint that always returns JSON data. For more information see the documentation on content negotiation .",
"title": "How the renderer is determined"
- },
+ },
{
- "location": "/api-guide/renderers/#setting-the-renderers",
- "text": "The default set of renderers may be set globally, using the DEFAULT_RENDERER_CLASSES setting. For example, the following settings would use JSON as the main media type and also include the self describing API. REST_FRAMEWORK = {\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework.renderers.JSONRenderer',\n 'rest_framework.renderers.BrowsableAPIRenderer',\n )\n} You can also set the renderers used for an individual view, or viewset,\nusing the APIView class-based views. from django.contrib.auth.models import User\nfrom rest_framework.renderers import JSONRenderer\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\n\nclass UserCountView(APIView):\n \"\"\"\n A view that returns the count of active users in JSON.\n \"\"\"\n renderer_classes = (JSONRenderer, )\n\n def get(self, request, format=None):\n user_count = User.objects.filter(active=True).count()\n content = {'user_count': user_count}\n return Response(content) Or, if you're using the @api_view decorator with function based views. @api_view(['GET'])\n@renderer_classes((JSONRenderer,))\ndef user_count_view(request, format=None):\n \"\"\"\n A view that returns the count of active users in JSON.\n \"\"\"\n user_count = User.objects.filter(active=True).count()\n content = {'user_count': user_count}\n return Response(content)",
+ "location": "/api-guide/renderers/#setting-the-renderers",
+ "text": "The default set of renderers may be set globally, using the DEFAULT_RENDERER_CLASSES setting. For example, the following settings would use JSON as the main media type and also include the self describing API. REST_FRAMEWORK = {\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework.renderers.JSONRenderer',\n 'rest_framework.renderers.BrowsableAPIRenderer',\n )\n} You can also set the renderers used for an individual view, or viewset,\nusing the APIView class-based views. from django.contrib.auth.models import User\nfrom rest_framework.renderers import JSONRenderer\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\n\nclass UserCountView(APIView):\n \"\"\"\n A view that returns the count of active users in JSON.\n \"\"\"\n renderer_classes = (JSONRenderer, )\n\n def get(self, request, format=None):\n user_count = User.objects.filter(active=True).count()\n content = {'user_count': user_count}\n return Response(content) Or, if you're using the @api_view decorator with function based views. @api_view(['GET'])\n@renderer_classes((JSONRenderer,))\ndef user_count_view(request, format=None):\n \"\"\"\n A view that returns the count of active users in JSON.\n \"\"\"\n user_count = User.objects.filter(active=True).count()\n content = {'user_count': user_count}\n return Response(content)",
"title": "Setting the renderers"
- },
+ },
{
- "location": "/api-guide/renderers/#ordering-of-renderer-classes",
- "text": "It's important when specifying the renderer classes for your API to think about what priority you want to assign to each media type. If a client underspecifies the representations it can accept, such as sending an Accept: */* header, or not including an Accept header at all, then REST framework will select the first renderer in the list to use for the response. For example if your API serves JSON responses and the HTML browsable API, you might want to make JSONRenderer your default renderer, in order to send JSON responses to clients that do not specify an Accept header. If your API includes views that can serve both regular webpages and API responses depending on the request, then you might consider making TemplateHTMLRenderer your default renderer, in order to play nicely with older browsers that send broken accept headers .",
+ "location": "/api-guide/renderers/#ordering-of-renderer-classes",
+ "text": "It's important when specifying the renderer classes for your API to think about what priority you want to assign to each media type. If a client underspecifies the representations it can accept, such as sending an Accept: */* header, or not including an Accept header at all, then REST framework will select the first renderer in the list to use for the response. For example if your API serves JSON responses and the HTML browsable API, you might want to make JSONRenderer your default renderer, in order to send JSON responses to clients that do not specify an Accept header. If your API includes views that can serve both regular webpages and API responses depending on the request, then you might consider making TemplateHTMLRenderer your default renderer, in order to play nicely with older browsers that send broken accept headers .",
"title": "Ordering of renderer classes"
- },
+ },
{
- "location": "/api-guide/renderers/#api-reference",
- "text": "",
+ "location": "/api-guide/renderers/#api-reference",
+ "text": "",
"title": "API Reference"
- },
+ },
{
- "location": "/api-guide/renderers/#jsonrenderer",
- "text": "Renders the request data into JSON , using utf-8 encoding. Note that the default style is to include unicode characters, and render the response using a compact style with no unnecessary whitespace: {\"unicode black star\":\"\u2605\",\"value\":999} The client may additionally include an 'indent' media type parameter, in which case the returned JSON will be indented. For example Accept: application/json; indent=4 . {\n \"unicode black star\": \"\u2605\",\n \"value\": 999\n} The default JSON encoding style can be altered using the UNICODE_JSON and COMPACT_JSON settings keys. .media_type : application/json .format : 'json' .charset : None",
+ "location": "/api-guide/renderers/#jsonrenderer",
+ "text": "Renders the request data into JSON , using utf-8 encoding. Note that the default style is to include unicode characters, and render the response using a compact style with no unnecessary whitespace: {\"unicode black star\":\"\u2605\",\"value\":999} The client may additionally include an 'indent' media type parameter, in which case the returned JSON will be indented. For example Accept: application/json; indent=4 . {\n \"unicode black star\": \"\u2605\",\n \"value\": 999\n} The default JSON encoding style can be altered using the UNICODE_JSON and COMPACT_JSON settings keys. .media_type : application/json .format : 'json' .charset : None",
"title": "JSONRenderer"
- },
+ },
{
- "location": "/api-guide/renderers/#templatehtmlrenderer",
- "text": "Renders data to HTML, using Django's standard template rendering.\nUnlike other renderers, the data passed to the Response does not need to be serialized. Also, unlike other renderers, you may want to include a template_name argument when creating the Response . The TemplateHTMLRenderer will create a RequestContext , using the response.data as the context dict, and determine a template name to use to render the context. The template name is determined by (in order of preference): An explicit template_name argument passed to the response. An explicit .template_name attribute set on this class. The return result of calling view.get_template_names() . An example of a view that uses TemplateHTMLRenderer : class UserDetail(generics.RetrieveAPIView):\n \"\"\"\n A view that returns a templated HTML representation of a given user.\n \"\"\"\n queryset = User.objects.all()\n renderer_classes = (TemplateHTMLRenderer,)\n\n def get(self, request, *args, **kwargs):\n self.object = self.get_object()\n return Response({'user': self.object}, template_name='user_detail.html') You can use TemplateHTMLRenderer either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint. 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 for further examples of TemplateHTMLRenderer usage. .media_type : text/html .format : 'html' .charset : utf-8 See also: StaticHTMLRenderer",
+ "location": "/api-guide/renderers/#templatehtmlrenderer",
+ "text": "Renders data to HTML, using Django's standard template rendering.\nUnlike other renderers, the data passed to the Response does not need to be serialized. Also, unlike other renderers, you may want to include a template_name argument when creating the Response . The TemplateHTMLRenderer will create a RequestContext , using the response.data as the context dict, and determine a template name to use to render the context. The template name is determined by (in order of preference): An explicit template_name argument passed to the response. An explicit .template_name attribute set on this class. The return result of calling view.get_template_names() . An example of a view that uses TemplateHTMLRenderer : class UserDetail(generics.RetrieveAPIView):\n \"\"\"\n A view that returns a templated HTML representation of a given user.\n \"\"\"\n queryset = User.objects.all()\n renderer_classes = (TemplateHTMLRenderer,)\n\n def get(self, request, *args, **kwargs):\n self.object = self.get_object()\n return Response({'user': self.object}, template_name='user_detail.html') You can use TemplateHTMLRenderer either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint. 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 for further examples of TemplateHTMLRenderer usage. .media_type : text/html .format : 'html' .charset : utf-8 See also: StaticHTMLRenderer",
"title": "TemplateHTMLRenderer"
- },
+ },
{
- "location": "/api-guide/renderers/#statichtmlrenderer",
- "text": "A simple renderer that simply returns pre-rendered HTML. Unlike other renderers, the data passed to the response object should be a string representing the content to be returned. An example of a view that uses StaticHTMLRenderer : @api_view(('GET',))\n@renderer_classes((StaticHTMLRenderer,))\ndef simple_html_view(request):\n data = 'Hello, world
'\n return Response(data) You can use StaticHTMLRenderer either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint. .media_type : text/html .format : 'html' .charset : utf-8 See also: TemplateHTMLRenderer",
+ "location": "/api-guide/renderers/#statichtmlrenderer",
+ "text": "A simple renderer that simply returns pre-rendered HTML. Unlike other renderers, the data passed to the response object should be a string representing the content to be returned. An example of a view that uses StaticHTMLRenderer : @api_view(('GET',))\n@renderer_classes((StaticHTMLRenderer,))\ndef simple_html_view(request):\n data = ' html body h1 Hello, world /h1 /body /html '\n return Response(data) You can use StaticHTMLRenderer either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint. .media_type : text/html .format : 'html' .charset : utf-8 See also: TemplateHTMLRenderer",
"title": "StaticHTMLRenderer"
- },
+ },
{
- "location": "/api-guide/renderers/#browsableapirenderer",
- "text": "Renders data into HTML for the Browsable API: This renderer will determine which other renderer would have been given highest priority, and use that to display an API style response within the HTML page. .media_type : text/html .format : 'api' .charset : utf-8 .template : 'rest_framework/api.html'",
+ "location": "/api-guide/renderers/#browsableapirenderer",
+ "text": "Renders data into HTML for the Browsable API: This renderer will determine which other renderer would have been given highest priority, and use that to display an API style response within the HTML page. .media_type : text/html .format : 'api' .charset : utf-8 .template : 'rest_framework/api.html'",
"title": "BrowsableAPIRenderer"
- },
+ },
{
- "location": "/api-guide/renderers/#customizing-browsableapirenderer",
- "text": "By default the response content will be rendered with the highest priority renderer apart from BrowsableAPIRenderer . If you need to customize this behavior, for example to use HTML as the default return format, but use JSON in the browsable API, you can do so by overriding the get_default_renderer() method. For example: class CustomBrowsableAPIRenderer(BrowsableAPIRenderer):\n def get_default_renderer(self, view):\n return JSONRenderer()",
+ "location": "/api-guide/renderers/#customizing-browsableapirenderer",
+ "text": "By default the response content will be rendered with the highest priority renderer apart from BrowsableAPIRenderer . If you need to customize this behavior, for example to use HTML as the default return format, but use JSON in the browsable API, you can do so by overriding the get_default_renderer() method. For example: class CustomBrowsableAPIRenderer(BrowsableAPIRenderer):\n def get_default_renderer(self, view):\n return JSONRenderer()",
"title": "Customizing BrowsableAPIRenderer"
- },
+ },
{
- "location": "/api-guide/renderers/#adminrenderer",
- "text": "Renders data into HTML for an admin-like display: This renderer is suitable for CRUD-style web APIs that should also present a user-friendly interface for managing the data. Note that views that have nested or list serializers for their input won't work well with the AdminRenderer , as the HTML forms are unable to properly support them. Note : The AdminRenderer is only able to include links to detail pages when a properly configured URL_FIELD_NAME ( url by default) attribute is present in the data. For HyperlinkedModelSerializer this will be the case, but for ModelSerializer or plain Serializer classes you'll need to make sure to include the field explicitly. For example here we use models get_absolute_url method: class AccountSerializer(serializers.ModelSerializer):\n url = serializers.CharField(source='get_absolute_url', read_only=True)\n\n class Meta:\n model = Account .media_type : text/html .format : 'admin' .charset : utf-8 .template : 'rest_framework/admin.html'",
+ "location": "/api-guide/renderers/#adminrenderer",
+ "text": "Renders data into HTML for an admin-like display: This renderer is suitable for CRUD-style web APIs that should also present a user-friendly interface for managing the data. Note that views that have nested or list serializers for their input won't work well with the AdminRenderer , as the HTML forms are unable to properly support them. Note : The AdminRenderer is only able to include links to detail pages when a properly configured URL_FIELD_NAME ( url by default) attribute is present in the data. For HyperlinkedModelSerializer this will be the case, but for ModelSerializer or plain Serializer classes you'll need to make sure to include the field explicitly. For example here we use models get_absolute_url method: class AccountSerializer(serializers.ModelSerializer):\n url = serializers.CharField(source='get_absolute_url', read_only=True)\n\n class Meta:\n model = Account .media_type : text/html .format : 'admin' .charset : utf-8 .template : 'rest_framework/admin.html'",
"title": "AdminRenderer"
- },
+ },
{
- "location": "/api-guide/renderers/#htmlformrenderer",
- "text": "Renders data returned by a serializer into an HTML form. The output of this renderer does not include the enclosing For more information see the HTML & Forms documentation. .media_type : text/html .format : 'form' .charset : utf-8 .template : 'rest_framework/horizontal/form.html'",
+ "location": "/api-guide/renderers/#htmlformrenderer",
+ "text": "Renders data returned by a serializer into an HTML form. The output of this renderer does not include the enclosing form tags, a hidden CSRF input or any submit buttons. This renderer is not intended to be used directly, but can instead be used in templates by passing a serializer instance to the render_form template tag. {% load rest_framework %} form action=\"/submit-report/\" method=\"post\" \n {% csrf_token %}\n {% render_form serializer %}\n input type=\"submit\" value=\"Save\" / /form For more information see the HTML Forms documentation. .media_type : text/html .format : 'form' .charset : utf-8 .template : 'rest_framework/horizontal/form.html'",
"title": "HTMLFormRenderer"
- },
+ },
{
- "location": "/api-guide/renderers/#multipartrenderer",
- "text": "This renderer is used for rendering HTML multipart form data. It is not suitable as a response renderer , but is instead used for creating test requests, using REST framework's test client and test request factory . .media_type : multipart/form-data; boundary=BoUnDaRyStRiNg .format : 'multipart' .charset : utf-8",
+ "location": "/api-guide/renderers/#multipartrenderer",
+ "text": "This renderer is used for rendering HTML multipart form data. It is not suitable as a response renderer , but is instead used for creating test requests, using REST framework's test client and test request factory . .media_type : multipart/form-data; boundary=BoUnDaRyStRiNg .format : 'multipart' .charset : utf-8",
"title": "MultiPartRenderer"
- },
+ },
{
- "location": "/api-guide/renderers/#custom-renderers",
- "text": "To implement a custom renderer, you should override BaseRenderer , set the .media_type and .format properties, and implement the .render(self, data, media_type=None, renderer_context=None) method. The method should return a bytestring, which will be used as the body of the HTTP response. The arguments passed to the .render() method are:",
+ "location": "/api-guide/renderers/#custom-renderers",
+ "text": "To implement a custom renderer, you should override BaseRenderer , set the .media_type and .format properties, and implement the .render(self, data, media_type=None, renderer_context=None) method. The method should return a bytestring, which will be used as the body of the HTTP response. The arguments passed to the .render() method are:",
"title": "Custom renderers"
- },
+ },
{
- "location": "/api-guide/renderers/#data",
- "text": "The request data, as set by the Response() instantiation.",
+ "location": "/api-guide/renderers/#data",
+ "text": "The request data, as set by the Response() instantiation.",
"title": "data"
- },
+ },
{
- "location": "/api-guide/renderers/#media_typenone",
- "text": "Optional. If provided, this is the accepted media type, as determined by the content negotiation stage. Depending on the client's Accept: header, this may be more specific than the renderer's media_type attribute, and may include media type parameters. For example \"application/json; nested=true\" .",
+ "location": "/api-guide/renderers/#media_typenone",
+ "text": "Optional. If provided, this is the accepted media type, as determined by the content negotiation stage. Depending on the client's Accept: header, this may be more specific than the renderer's media_type attribute, and may include media type parameters. For example \"application/json; nested=true\" .",
"title": "media_type=None"
- },
+ },
{
- "location": "/api-guide/renderers/#renderer_contextnone",
- "text": "Optional. If provided, this is a dictionary of contextual information provided by the view. By default this will include the following keys: view , request , response , args , kwargs .",
+ "location": "/api-guide/renderers/#renderer_contextnone",
+ "text": "Optional. If provided, this is a dictionary of contextual information provided by the view. By default this will include the following keys: view , request , response , args , kwargs .",
"title": "renderer_context=None"
- },
+ },
{
- "location": "/api-guide/renderers/#example",
- "text": "The following is an example plaintext renderer that will return a response with the data parameter as the content of the response. from django.utils.encoding import smart_unicode\nfrom rest_framework import renderers\n\n\nclass PlainTextRenderer(renderers.BaseRenderer):\n media_type = 'text/plain'\n format = 'txt'\n\n def render(self, data, media_type=None, renderer_context=None):\n return data.encode(self.charset)",
+ "location": "/api-guide/renderers/#example",
+ "text": "The following is an example plaintext renderer that will return a response with the data parameter as the content of the response. from django.utils.encoding import smart_unicode\nfrom rest_framework import renderers\n\n\nclass PlainTextRenderer(renderers.BaseRenderer):\n media_type = 'text/plain'\n format = 'txt'\n\n def render(self, data, media_type=None, renderer_context=None):\n return data.encode(self.charset)",
"title": "Example"
- },
+ },
{
- "location": "/api-guide/renderers/#setting-the-character-set",
- "text": "By default renderer classes are assumed to be using the UTF-8 encoding. To use a different encoding, set the charset attribute on the renderer. class PlainTextRenderer(renderers.BaseRenderer):\n media_type = 'text/plain'\n format = 'txt'\n charset = 'iso-8859-1'\n\n def render(self, data, media_type=None, renderer_context=None):\n return data.encode(self.charset) Note that if a renderer class returns a unicode string, then the response content will be coerced into a bytestring by the Response class, with the charset attribute set on the renderer used to determine the encoding. If the renderer returns a bytestring representing raw binary content, you should set a charset value of None , which will ensure the Content-Type header of the response will not have a charset value set. In some cases you may also want to set the render_style attribute to 'binary' . Doing so will also ensure that the browsable API will not attempt to display the binary content as a string. class JPEGRenderer(renderers.BaseRenderer):\n media_type = 'image/jpeg'\n format = 'jpg'\n charset = None\n render_style = 'binary'\n\n def render(self, data, media_type=None, renderer_context=None):\n return data",
+ "location": "/api-guide/renderers/#setting-the-character-set",
+ "text": "By default renderer classes are assumed to be using the UTF-8 encoding. To use a different encoding, set the charset attribute on the renderer. class PlainTextRenderer(renderers.BaseRenderer):\n media_type = 'text/plain'\n format = 'txt'\n charset = 'iso-8859-1'\n\n def render(self, data, media_type=None, renderer_context=None):\n return data.encode(self.charset) Note that if a renderer class returns a unicode string, then the response content will be coerced into a bytestring by the Response class, with the charset attribute set on the renderer used to determine the encoding. If the renderer returns a bytestring representing raw binary content, you should set a charset value of None , which will ensure the Content-Type header of the response will not have a charset value set. In some cases you may also want to set the render_style attribute to 'binary' . Doing so will also ensure that the browsable API will not attempt to display the binary content as a string. class JPEGRenderer(renderers.BaseRenderer):\n media_type = 'image/jpeg'\n format = 'jpg'\n charset = None\n render_style = 'binary'\n\n def render(self, data, media_type=None, renderer_context=None):\n return data",
"title": "Setting the character set"
- },
+ },
{
- "location": "/api-guide/renderers/#advanced-renderer-usage",
- "text": "You can do some pretty flexible things using REST framework's renderers. Some examples... Provide either flat or nested representations from the same endpoint, depending on the requested media type. Serve both regular HTML webpages, and JSON based API responses from the same endpoints. Specify multiple types of HTML representation for API clients to use. Underspecify a renderer's media type, such as using media_type = 'image/*' , and use the Accept header to vary the encoding of the response.",
+ "location": "/api-guide/renderers/#advanced-renderer-usage",
+ "text": "You can do some pretty flexible things using REST framework's renderers. Some examples... Provide either flat or nested representations from the same endpoint, depending on the requested media type. Serve both regular HTML webpages, and JSON based API responses from the same endpoints. Specify multiple types of HTML representation for API clients to use. Underspecify a renderer's media type, such as using media_type = 'image/*' , and use the Accept header to vary the encoding of the response.",
"title": "Advanced renderer usage"
- },
+ },
{
- "location": "/api-guide/renderers/#varying-behaviour-by-media-type",
- "text": "In some cases you might want your view to use different serialization styles depending on the accepted media type. If you need to do this you can access request.accepted_renderer to determine the negotiated renderer that will be used for the response. For example: @api_view(('GET',))\n@renderer_classes((TemplateHTMLRenderer, JSONRenderer))\ndef list_users(request):\n \"\"\"\n A view that can return JSON or HTML representations\n of the users in the system.\n \"\"\"\n queryset = Users.objects.filter(active=True)\n\n if request.accepted_renderer.format == 'html':\n # TemplateHTMLRenderer takes a context dict,\n # and additionally requires a 'template_name'.\n # It does not require serialization.\n data = {'users': queryset}\n return Response(data, template_name='list_users.html')\n\n # JSONRenderer requires serialized data as normal.\n serializer = UserSerializer(instance=queryset)\n data = serializer.data\n return Response(data)",
+ "location": "/api-guide/renderers/#varying-behaviour-by-media-type",
+ "text": "In some cases you might want your view to use different serialization styles depending on the accepted media type. If you need to do this you can access request.accepted_renderer to determine the negotiated renderer that will be used for the response. For example: @api_view(('GET',))\n@renderer_classes((TemplateHTMLRenderer, JSONRenderer))\ndef list_users(request):\n \"\"\"\n A view that can return JSON or HTML representations\n of the users in the system.\n \"\"\"\n queryset = Users.objects.filter(active=True)\n\n if request.accepted_renderer.format == 'html':\n # TemplateHTMLRenderer takes a context dict,\n # and additionally requires a 'template_name'.\n # It does not require serialization.\n data = {'users': queryset}\n return Response(data, template_name='list_users.html')\n\n # JSONRenderer requires serialized data as normal.\n serializer = UserSerializer(instance=queryset)\n data = serializer.data\n return Response(data)",
"title": "Varying behaviour by media type"
- },
+ },
{
- "location": "/api-guide/renderers/#underspecifying-the-media-type",
- "text": "In some cases you might want a renderer to serve a range of media types.\nIn this case you can underspecify the media types it should respond to, by using a media_type value such as image/* , or */* . If you underspecify the renderer's media type, you should make sure to specify the media type explicitly when you return the response, using the content_type attribute. For example: return Response(data, content_type='image/png')",
+ "location": "/api-guide/renderers/#underspecifying-the-media-type",
+ "text": "In some cases you might want a renderer to serve a range of media types.\nIn this case you can underspecify the media types it should respond to, by using a media_type value such as image/* , or */* . If you underspecify the renderer's media type, you should make sure to specify the media type explicitly when you return the response, using the content_type attribute. For example: return Response(data, content_type='image/png')",
"title": "Underspecifying the media type"
- },
+ },
{
- "location": "/api-guide/renderers/#designing-your-media-types",
- "text": "For the purposes of many Web APIs, simple JSON responses with hyperlinked relations may be sufficient. If you want to fully embrace RESTful design and HATEOAS you'll need to consider the design and usage of your media types in more detail. In the words of Roy Fielding , \"A REST API should spend almost all of its descriptive effort in defining the media type(s) used for representing resources and driving application state, or in defining extended relation names and/or hypertext-enabled mark-up for existing standard media types.\". For good examples of custom media types, see GitHub's use of a custom application/vnd.github+json media type, and Mike Amundsen's IANA approved application/vnd.collection+json JSON-based hypermedia.",
+ "location": "/api-guide/renderers/#designing-your-media-types",
+ "text": "For the purposes of many Web APIs, simple JSON responses with hyperlinked relations may be sufficient. If you want to fully embrace RESTful design and HATEOAS you'll need to consider the design and usage of your media types in more detail. In the words of Roy Fielding , \"A REST API should spend almost all of its descriptive effort in defining the media type(s) used for representing resources and driving application state, or in defining extended relation names and/or hypertext-enabled mark-up for existing standard media types.\". For good examples of custom media types, see GitHub's use of a custom application/vnd.github+json media type, and Mike Amundsen's IANA approved application/vnd.collection+json JSON-based hypermedia.",
"title": "Designing your media types"
- },
+ },
{
- "location": "/api-guide/renderers/#html-error-views",
- "text": "Typically a renderer will behave the same regardless of if it's dealing with a regular response, or with a response caused by an exception being raised, such as an Http404 or PermissionDenied exception, or a subclass of APIException . If you're using either the TemplateHTMLRenderer or the StaticHTMLRenderer and an exception is raised, the behavior is slightly different, and mirrors Django's default handling of error views . Exceptions raised and handled by an HTML renderer will attempt to render using one of the following methods, by order of precedence. Load and render a template named {status_code}.html . Load and render a template named api_exception.html . Render the HTTP status code and text, for example \"404 Not Found\". Templates will render with a RequestContext which includes the status_code and details keys. Note : If DEBUG=True , Django's standard traceback error page will be displayed instead of rendering the HTTP status code and text.",
+ "location": "/api-guide/renderers/#html-error-views",
+ "text": "Typically a renderer will behave the same regardless of if it's dealing with a regular response, or with a response caused by an exception being raised, such as an Http404 or PermissionDenied exception, or a subclass of APIException . If you're using either the TemplateHTMLRenderer or the StaticHTMLRenderer and an exception is raised, the behavior is slightly different, and mirrors Django's default handling of error views . Exceptions raised and handled by an HTML renderer will attempt to render using one of the following methods, by order of precedence. Load and render a template named {status_code}.html . Load and render a template named api_exception.html . Render the HTTP status code and text, for example \"404 Not Found\". Templates will render with a RequestContext which includes the status_code and details keys. Note : If DEBUG=True , Django's standard traceback error page will be displayed instead of rendering the HTTP status code and text.",
"title": "HTML error views"
- },
+ },
{
- "location": "/api-guide/renderers/#third-party-packages",
- "text": "The following third party packages are also available.",
+ "location": "/api-guide/renderers/#third-party-packages",
+ "text": "The following third party packages are also available.",
"title": "Third party packages"
- },
+ },
{
- "location": "/api-guide/renderers/#yaml",
- "text": "REST framework YAML provides YAML parsing and rendering support. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.",
+ "location": "/api-guide/renderers/#yaml",
+ "text": "REST framework YAML provides YAML parsing and rendering support. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.",
"title": "YAML"
- },
+ },
{
- "location": "/api-guide/renderers/#installation-configuration",
- "text": "Install using pip. $ pip install djangorestframework-yaml Modify your REST framework settings. REST_FRAMEWORK = {\n 'DEFAULT_PARSER_CLASSES': (\n 'rest_framework_yaml.parsers.YAMLParser',\n ),\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework_yaml.renderers.YAMLRenderer',\n ),\n}",
+ "location": "/api-guide/renderers/#installation-configuration",
+ "text": "Install using pip. $ pip install djangorestframework-yaml Modify your REST framework settings. REST_FRAMEWORK = {\n 'DEFAULT_PARSER_CLASSES': (\n 'rest_framework_yaml.parsers.YAMLParser',\n ),\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework_yaml.renderers.YAMLRenderer',\n ),\n}",
"title": "Installation & configuration"
- },
+ },
{
- "location": "/api-guide/renderers/#xml",
- "text": "REST Framework XML provides a simple informal XML format. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.",
+ "location": "/api-guide/renderers/#xml",
+ "text": "REST Framework XML provides a simple informal XML format. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.",
"title": "XML"
- },
+ },
{
- "location": "/api-guide/renderers/#installation-configuration_1",
- "text": "Install using pip. $ pip install djangorestframework-xml Modify your REST framework settings. REST_FRAMEWORK = {\n 'DEFAULT_PARSER_CLASSES': (\n 'rest_framework_xml.parsers.XMLParser',\n ),\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework_xml.renderers.XMLRenderer',\n ),\n}",
+ "location": "/api-guide/renderers/#installation-configuration_1",
+ "text": "Install using pip. $ pip install djangorestframework-xml Modify your REST framework settings. REST_FRAMEWORK = {\n 'DEFAULT_PARSER_CLASSES': (\n 'rest_framework_xml.parsers.XMLParser',\n ),\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework_xml.renderers.XMLRenderer',\n ),\n}",
"title": "Installation & configuration"
- },
+ },
{
- "location": "/api-guide/renderers/#jsonp",
- "text": "REST framework JSONP provides JSONP rendering support. It was previously included directly in the REST framework package, and is now instead supported as a third-party package. Warning : If you require cross-domain AJAX requests, you should generally be using the more modern approach of CORS as an alternative to JSONP . See the CORS documentation for more details. The jsonp approach is essentially a browser hack, and is only appropriate for globally readable API endpoints , where GET requests are unauthenticated and do not require any user permissions.",
+ "location": "/api-guide/renderers/#jsonp",
+ "text": "REST framework JSONP provides JSONP rendering support. It was previously included directly in the REST framework package, and is now instead supported as a third-party package. Warning : If you require cross-domain AJAX requests, you should generally be using the more modern approach of CORS as an alternative to JSONP . See the CORS documentation for more details. The jsonp approach is essentially a browser hack, and is only appropriate for globally readable API endpoints , where GET requests are unauthenticated and do not require any user permissions.",
"title": "JSONP"
- },
+ },
{
- "location": "/api-guide/renderers/#installation-configuration_2",
- "text": "Install using pip. $ pip install djangorestframework-jsonp Modify your REST framework settings. REST_FRAMEWORK = {\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework_jsonp.renderers.JSONPRenderer',\n ),\n}",
+ "location": "/api-guide/renderers/#installation-configuration_2",
+ "text": "Install using pip. $ pip install djangorestframework-jsonp Modify your REST framework settings. REST_FRAMEWORK = {\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework_jsonp.renderers.JSONPRenderer',\n ),\n}",
"title": "Installation & configuration"
- },
+ },
{
- "location": "/api-guide/renderers/#messagepack",
- "text": "MessagePack is a fast, efficient binary serialization format. Juan Riaza maintains the djangorestframework-msgpack package which provides MessagePack renderer and parser support for REST framework.",
+ "location": "/api-guide/renderers/#messagepack",
+ "text": "MessagePack is a fast, efficient binary serialization format. Juan Riaza maintains the djangorestframework-msgpack package which provides MessagePack renderer and parser support for REST framework.",
"title": "MessagePack"
- },
+ },
{
- "location": "/api-guide/renderers/#xlsx-binary-spreadsheet-endpoints",
- "text": "XLSX is the world's most popular binary spreadsheet format. Tim Allen of The Wharton School maintains drf-renderer-xlsx , which renders an endpoint as an XLSX spreadsheet using OpenPyXL, and allows the client to download it. Spreadsheets can be styled on a per-view basis.",
+ "location": "/api-guide/renderers/#xlsx-binary-spreadsheet-endpoints",
+ "text": "XLSX is the world's most popular binary spreadsheet format. Tim Allen of The Wharton School maintains drf-renderer-xlsx , which renders an endpoint as an XLSX spreadsheet using OpenPyXL, and allows the client to download it. Spreadsheets can be styled on a per-view basis.",
"title": "XLSX (Binary Spreadsheet Endpoints)"
- },
+ },
{
- "location": "/api-guide/renderers/#installation-configuration_3",
- "text": "Install using pip. $ pip install drf-renderer-xlsx Modify your REST framework settings. REST_FRAMEWORK = {\n ...\n\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework.renderers.JSONRenderer',\n 'rest_framework.renderers.BrowsableAPIRenderer',\n 'drf_renderer_xlsx.renderers.XLSXRenderer',\n ),\n} To avoid having a file streamed without a filename (which the browser will often default to the filename \"download\", with no extension), we need to use a mixin to override the Content-Disposition header. If no filename is provided, it will default to export.xlsx . For example: from rest_framework.viewsets import ReadOnlyModelViewSet\nfrom drf_renderer_xlsx.mixins import XLSXFileMixin\nfrom drf_renderer_xlsx.renderers import XLSXRenderer\n\nfrom .models import MyExampleModel\nfrom .serializers import MyExampleSerializer\n\nclass MyExampleViewSet(XLSXFileMixin, ReadOnlyModelViewSet):\n queryset = MyExampleModel.objects.all()\n serializer_class = MyExampleSerializer\n renderer_classes = (XLSXRenderer,)\n filename = 'my_export.xlsx'",
+ "location": "/api-guide/renderers/#installation-configuration_3",
+ "text": "Install using pip. $ pip install drf-renderer-xlsx Modify your REST framework settings. REST_FRAMEWORK = {\n ...\n\n 'DEFAULT_RENDERER_CLASSES': (\n 'rest_framework.renderers.JSONRenderer',\n 'rest_framework.renderers.BrowsableAPIRenderer',\n 'drf_renderer_xlsx.renderers.XLSXRenderer',\n ),\n} To avoid having a file streamed without a filename (which the browser will often default to the filename \"download\", with no extension), we need to use a mixin to override the Content-Disposition header. If no filename is provided, it will default to export.xlsx . For example: from rest_framework.viewsets import ReadOnlyModelViewSet\nfrom drf_renderer_xlsx.mixins import XLSXFileMixin\nfrom drf_renderer_xlsx.renderers import XLSXRenderer\n\nfrom .models import MyExampleModel\nfrom .serializers import MyExampleSerializer\n\nclass MyExampleViewSet(XLSXFileMixin, ReadOnlyModelViewSet):\n queryset = MyExampleModel.objects.all()\n serializer_class = MyExampleSerializer\n renderer_classes = (XLSXRenderer,)\n filename = 'my_export.xlsx'",
"title": "Installation & configuration"
- },
+ },
{
- "location": "/api-guide/renderers/#csv",
- "text": "Comma-separated values are a plain-text tabular data format, that can be easily imported into spreadsheet applications. Mjumbe Poe maintains the djangorestframework-csv package which provides CSV renderer support for REST framework.",
+ "location": "/api-guide/renderers/#csv",
+ "text": "Comma-separated values are a plain-text tabular data format, that can be easily imported into spreadsheet applications. Mjumbe Poe maintains the djangorestframework-csv package which provides CSV renderer support for REST framework.",
"title": "CSV"
- },
+ },
{
- "location": "/api-guide/renderers/#ultrajson",
- "text": "UltraJSON is an optimized C JSON encoder which can give significantly faster JSON rendering. Jacob Haslehurst maintains the drf-ujson-renderer package which implements JSON rendering using the UJSON package.",
+ "location": "/api-guide/renderers/#ultrajson",
+ "text": "UltraJSON is an optimized C JSON encoder which can give significantly faster JSON rendering. Jacob Haslehurst maintains the drf-ujson-renderer package which implements JSON rendering using the UJSON package.",
"title": "UltraJSON"
- },
+ },
{
- "location": "/api-guide/renderers/#camelcase-json",
- "text": "djangorestframework-camel-case provides camel case JSON renderers and parsers for REST framework. This allows serializers to use Python-style underscored field names, but be exposed in the API as Javascript-style camel case field names. It is maintained by Vitaly Babiy .",
+ "location": "/api-guide/renderers/#camelcase-json",
+ "text": "djangorestframework-camel-case provides camel case JSON renderers and parsers for REST framework. This allows serializers to use Python-style underscored field names, but be exposed in the API as Javascript-style camel case field names. It is maintained by Vitaly Babiy .",
"title": "CamelCase JSON"
- },
+ },
{
- "location": "/api-guide/renderers/#pandas-csv-excel-png",
- "text": "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 as part of the wq Project .",
+ "location": "/api-guide/renderers/#pandas-csv-excel-png",
+ "text": "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 as part of the wq Project .",
"title": "Pandas (CSV, Excel, PNG)"
- },
+ },
{
- "location": "/api-guide/renderers/#latex",
- "text": "Rest Framework Latex provides a renderer that outputs PDFs using Laulatex. It is maintained by Pebble (S/F Software) .",
+ "location": "/api-guide/renderers/#latex",
+ "text": "Rest Framework Latex provides a renderer that outputs PDFs using Laulatex. It is maintained by Pebble (S/F Software) .",
"title": "LaTeX"
- },
+ },
{
- "location": "/api-guide/serializers/",
- "text": "Serializers\n\n\n\n\nExpanding the usefulness of the serializers is something that we would\nlike to address. However, it's not a trivial problem, and it\nwill take some serious design work.\n\n\n\u2014 Russell Keith-Magee, \nDjango users group\n\n\n\n\nSerializers allow complex data such as querysets and model instances to be converted to native Python datatypes that can then be easily rendered into \nJSON\n, \nXML\n or other content types. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.\n\n\nThe serializers in REST framework work very similarly to Django's \nForm\n and \nModelForm\n classes. We provide a \nSerializer\n class which gives you a powerful, generic way to control the output of your responses, as well as a \nModelSerializer\n class which provides a useful shortcut for creating serializers that deal with model instances and querysets.\n\n\nDeclaring Serializers\n\n\nLet's start by creating a simple object we can use for example purposes:\n\n\nfrom datetime import datetime\n\nclass Comment(object):\n def __init__(self, email, content, created=None):\n self.email = email\n self.content = content\n self.created = created or datetime.now()\n\ncomment = Comment(email='leila@example.com', content='foo bar')\n\n\n\nWe'll declare a serializer that we can use to serialize and deserialize data that corresponds to \nComment\n objects.\n\n\nDeclaring a serializer looks very similar to declaring a form:\n\n\nfrom rest_framework import serializers\n\nclass CommentSerializer(serializers.Serializer):\n email = serializers.EmailField()\n content = serializers.CharField(max_length=200)\n created = serializers.DateTimeField()\n\n\n\nSerializing objects\n\n\nWe can now use \nCommentSerializer\n to serialize a comment, or list of comments. Again, using the \nSerializer\n class looks a lot like using a \nForm\n class.\n\n\nserializer = CommentSerializer(comment)\nserializer.data\n# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}\n\n\n\nAt this point we've translated the model instance into Python native datatypes. To finalise the serialization process we render the data into \njson\n.\n\n\nfrom rest_framework.renderers import JSONRenderer\n\njson = JSONRenderer().render(serializer.data)\njson\n# b'{\"email\":\"leila@example.com\",\"content\":\"foo bar\",\"created\":\"2016-01-27T15:17:10.375877\"}'\n\n\n\nDeserializing objects\n\n\nDeserialization is similar. First we parse a stream into Python native datatypes...\n\n\nimport io\nfrom rest_framework.parsers import JSONParser\n\nstream = io.BytesIO(json)\ndata = JSONParser().parse(stream)\n\n\n\n...then we restore those native datatypes into a dictionary of validated data.\n\n\nserializer = CommentSerializer(data=data)\nserializer.is_valid()\n# True\nserializer.validated_data\n# {'content': 'foo bar', 'email': 'leila@example.com', 'created': datetime.datetime(2012, 08, 22, 16, 20, 09, 822243)}\n\n\n\nSaving instances\n\n\nIf we want to be able to return complete object instances based on the validated data we need to implement one or both of the \n.create()\n and \n.update()\n methods. For example:\n\n\nclass CommentSerializer(serializers.Serializer):\n email = serializers.EmailField()\n content = serializers.CharField(max_length=200)\n created = serializers.DateTimeField()\n\n def create(self, validated_data):\n return Comment(**validated_data)\n\n def update(self, instance, validated_data):\n instance.email = validated_data.get('email', instance.email)\n instance.content = validated_data.get('content', instance.content)\n instance.created = validated_data.get('created', instance.created)\n return instance\n\n\n\nIf your object instances correspond to Django models you'll also want to ensure that these methods save the object to the database. For example, if \nComment\n was a Django model, the methods might look like this:\n\n\n def create(self, validated_data):\n return Comment.objects.create(**validated_data)\n\n def update(self, instance, validated_data):\n instance.email = validated_data.get('email', instance.email)\n instance.content = validated_data.get('content', instance.content)\n instance.created = validated_data.get('created', instance.created)\n instance.save()\n return instance\n\n\n\nNow when deserializing data, we can call \n.save()\n to return an object instance, based on the validated data.\n\n\ncomment = serializer.save()\n\n\n\nCalling \n.save()\n will either create a new instance, or update an existing instance, depending on if an existing instance was passed when instantiating the serializer class:\n\n\n# .save() will create a new instance.\nserializer = CommentSerializer(data=data)\n\n# .save() will update the existing `comment` instance.\nserializer = CommentSerializer(comment, data=data)\n\n\n\nBoth the \n.create()\n and \n.update()\n methods are optional. You can implement either neither, one, or both of them, depending on the use-case for your serializer class.\n\n\nPassing additional attributes to \n.save()\n\n\nSometimes you'll want your view code to be able to inject additional data at the point of saving the instance. This additional data might include information like the current user, the current time, or anything else that is not part of the request data.\n\n\nYou can do so by including additional keyword arguments when calling \n.save()\n. For example:\n\n\nserializer.save(owner=request.user)\n\n\n\nAny additional keyword arguments will be included in the \nvalidated_data\n argument when \n.create()\n or \n.update()\n are called.\n\n\nOverriding \n.save()\n directly.\n\n\nIn some cases the \n.create()\n and \n.update()\n method names may not be meaningful. For example, in a contact form we may not be creating new instances, but instead sending an email or other message.\n\n\nIn these cases you might instead choose to override \n.save()\n directly, as being more readable and meaningful.\n\n\nFor example:\n\n\nclass ContactForm(serializers.Serializer):\n email = serializers.EmailField()\n message = serializers.CharField()\n\n def save(self):\n email = self.validated_data['email']\n message = self.validated_data['message']\n send_email(from=email, message=message)\n\n\n\nNote that in the case above we're now having to access the serializer \n.validated_data\n property directly.\n\n\nValidation\n\n\nWhen deserializing data, you always need to call \nis_valid()\n before attempting to access the validated data, or save an object instance. If any validation errors occur, the \n.errors\n property will contain a dictionary representing the resulting error messages. For example:\n\n\nserializer = CommentSerializer(data={'email': 'foobar', 'content': 'baz'})\nserializer.is_valid()\n# False\nserializer.errors\n# {'email': ['Enter a valid e-mail address.'], 'created': ['This field is required.']}\n\n\n\nEach key in the dictionary will be the field name, and the values will be lists of strings of any error messages corresponding to that field. The \nnon_field_errors\n key may also be present, and will list any general validation errors. The name of the \nnon_field_errors\n key may be customized using the \nNON_FIELD_ERRORS_KEY\n REST framework setting.\n\n\nWhen deserializing a list of items, errors will be returned as a list of dictionaries representing each of the deserialized items.\n\n\nRaising an exception on invalid data\n\n\nThe \n.is_valid()\n method takes an optional \nraise_exception\n flag that will cause it to raise a \nserializers.ValidationError\n exception if there are validation errors.\n\n\nThese exceptions are automatically dealt with by the default exception handler that REST framework provides, and will return \nHTTP 400 Bad Request\n responses by default.\n\n\n# Return a 400 response if the data was invalid.\nserializer.is_valid(raise_exception=True)\n\n\n\nField-level validation\n\n\nYou can specify custom field-level validation by adding \n.validate_\n methods to your \nSerializer\n subclass. These are similar to the \n.clean_\n methods on Django forms.\n\n\nThese methods take a single argument, which is the field value that requires validation.\n\n\nYour \nvalidate_\n methods should return the validated value or raise a \nserializers.ValidationError\n. For example:\n\n\nfrom rest_framework import serializers\n\nclass BlogPostSerializer(serializers.Serializer):\n title = serializers.CharField(max_length=100)\n content = serializers.CharField()\n\n def validate_title(self, value):\n \"\"\"\n Check that the blog post is about Django.\n \"\"\"\n if 'django' not in value.lower():\n raise serializers.ValidationError(\"Blog post is not about Django\")\n return value\n\n\n\n\n\nNote:\n If your \n\n is declared on your serializer with the parameter \nrequired=False\n then this validation step will not take place if the field is not included.\n\n\n\n\nObject-level validation\n\n\nTo do any other validation that requires access to multiple fields, add a method called \n.validate()\n to your \nSerializer\n subclass. This method takes a single argument, which is a dictionary of field values. It should raise a \nserializers.ValidationError\n if necessary, or just return the validated values. For example:\n\n\nfrom rest_framework import serializers\n\nclass EventSerializer(serializers.Serializer):\n description = serializers.CharField(max_length=100)\n start = serializers.DateTimeField()\n finish = serializers.DateTimeField()\n\n def validate(self, data):\n \"\"\"\n Check that start is before finish.\n \"\"\"\n if data['start'] > data['finish']:\n raise serializers.ValidationError(\"finish must occur after start\")\n return data\n\n\n\nValidators\n\n\nIndividual fields on a serializer can include validators, by declaring them on the field instance, for example:\n\n\ndef multiple_of_ten(value):\n if value % 10 != 0:\n raise serializers.ValidationError('Not a multiple of ten')\n\nclass GameRecord(serializers.Serializer):\n score = IntegerField(validators=[multiple_of_ten])\n ...\n\n\n\nSerializer classes can also include reusable validators that are applied to the complete set of field data. These validators are included by declaring them on an inner \nMeta\n class, like so:\n\n\nclass EventSerializer(serializers.Serializer):\n name = serializers.CharField()\n room_number = serializers.IntegerField(choices=[101, 102, 103, 201])\n date = serializers.DateField()\n\n class Meta:\n # Each room only has one event per day.\n validators = UniqueTogetherValidator(\n queryset=Event.objects.all(),\n fields=['room_number', 'date']\n )\n\n\n\nFor more information see the \nvalidators documentation\n.\n\n\nAccessing the initial data and instance\n\n\nWhen passing an initial object or queryset to a serializer instance, the object will be made available as \n.instance\n. If no initial object is passed then the \n.instance\n attribute will be \nNone\n.\n\n\nWhen passing data to a serializer instance, the unmodified data will be made available as \n.initial_data\n. If the data keyword argument is not passed then the \n.initial_data\n attribute will not exist.\n\n\nPartial updates\n\n\nBy default, serializers must be passed values for all required fields or they will raise validation errors. You can use the \npartial\n argument in order to allow partial updates.\n\n\n# Update `comment` with partial data\nserializer = CommentSerializer(comment, data={'content': 'foo bar'}, partial=True)\n\n\n\nDealing with nested objects\n\n\nThe previous examples are fine for dealing with objects that only have simple datatypes, but sometimes we also need to be able to represent more complex objects, where some of the attributes of an object might not be simple datatypes such as strings, dates or integers.\n\n\nThe \nSerializer\n class is itself a type of \nField\n, and can be used to represent relationships where one object type is nested inside another.\n\n\nclass UserSerializer(serializers.Serializer):\n email = serializers.EmailField()\n username = serializers.CharField(max_length=100)\n\nclass CommentSerializer(serializers.Serializer):\n user = UserSerializer()\n content = serializers.CharField(max_length=200)\n created = serializers.DateTimeField()\n\n\n\nIf a nested representation may optionally accept the \nNone\n value you should pass the \nrequired=False\n flag to the nested serializer.\n\n\nclass CommentSerializer(serializers.Serializer):\n user = UserSerializer(required=False) # May be an anonymous user.\n content = serializers.CharField(max_length=200)\n created = serializers.DateTimeField()\n\n\n\nSimilarly if a nested representation should be a list of items, you should pass the \nmany=True\n flag to the nested serialized.\n\n\nclass CommentSerializer(serializers.Serializer):\n user = UserSerializer(required=False)\n edits = EditItemSerializer(many=True) # A nested list of 'edit' items.\n content = serializers.CharField(max_length=200)\n created = serializers.DateTimeField()\n\n\n\nWritable nested representations\n\n\nWhen dealing with nested representations that support deserializing the data, any errors with nested objects will be nested under the field name of the nested object.\n\n\nserializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})\nserializer.is_valid()\n# False\nserializer.errors\n# {'user': {'email': ['Enter a valid e-mail address.']}, 'created': ['This field is required.']}\n\n\n\nSimilarly, the \n.validated_data\n property will include nested data structures.\n\n\nWriting \n.create()\n methods for nested representations\n\n\nIf you're supporting writable nested representations you'll need to write \n.create()\n or \n.update()\n methods that handle saving multiple objects.\n\n\nThe following example demonstrates how you might handle creating a user with a nested profile object.\n\n\nclass UserSerializer(serializers.ModelSerializer):\n profile = ProfileSerializer()\n\n class Meta:\n model = User\n fields = ('username', 'email', 'profile')\n\n def create(self, validated_data):\n profile_data = validated_data.pop('profile')\n user = User.objects.create(**validated_data)\n Profile.objects.create(user=user, **profile_data)\n return user\n\n\n\nWriting \n.update()\n methods for nested representations\n\n\nFor updates you'll want to think carefully about how to handle updates to relationships. For example if the data for the relationship is \nNone\n, or not provided, which of the following should occur?\n\n\n\n\nSet the relationship to \nNULL\n in the database.\n\n\nDelete the associated instance.\n\n\nIgnore the data and leave the instance as it is.\n\n\nRaise a validation error.\n\n\n\n\nHere's an example for an \n.update()\n method on our previous \nUserSerializer\n class.\n\n\n def update(self, instance, validated_data):\n profile_data = validated_data.pop('profile')\n # Unless the application properly enforces that this field is\n # always set, the follow could raise a `DoesNotExist`, which\n # would need to be handled.\n profile = instance.profile\n\n instance.username = validated_data.get('username', instance.username)\n instance.email = validated_data.get('email', instance.email)\n instance.save()\n\n profile.is_premium_member = profile_data.get(\n 'is_premium_member',\n profile.is_premium_member\n )\n profile.has_support_contract = profile_data.get(\n 'has_support_contract',\n profile.has_support_contract\n )\n profile.save()\n\n return instance\n\n\n\nBecause the behavior of nested creates and updates can be ambiguous, and may require complex dependencies between related models, REST framework 3 requires you to always write these methods explicitly. The default \nModelSerializer\n \n.create()\n and \n.update()\n methods do not include support for writable nested representations.\n\n\nThere are however, third-party packages available such as \nDRF Writable Nested\n that support automatic writable nested representations.\n\n\nHandling saving related instances in model manager classes\n\n\nAn alternative to saving multiple related instances in the serializer is to write custom model manager classes that handle creating the correct instances.\n\n\nFor example, suppose we wanted to ensure that \nUser\n instances and \nProfile\n instances are always created together as a pair. We might write a custom manager class that looks something like this:\n\n\nclass UserManager(models.Manager):\n ...\n\n def create(self, username, email, is_premium_member=False, has_support_contract=False):\n user = User(username=username, email=email)\n user.save()\n profile = Profile(\n user=user,\n is_premium_member=is_premium_member,\n has_support_contract=has_support_contract\n )\n profile.save()\n return user\n\n\n\nThis manager class now more nicely encapsulates that user instances and profile instances are always created at the same time. Our \n.create()\n method on the serializer class can now be re-written to use the new manager method.\n\n\ndef create(self, validated_data):\n return User.objects.create(\n username=validated_data['username'],\n email=validated_data['email']\n is_premium_member=validated_data['profile']['is_premium_member']\n has_support_contract=validated_data['profile']['has_support_contract']\n )\n\n\n\nFor more details on this approach see the Django documentation on \nmodel managers\n, and \nthis blogpost on using model and manager classes\n.\n\n\nDealing with multiple objects\n\n\nThe \nSerializer\n class can also handle serializing or deserializing lists of objects.\n\n\nSerializing multiple objects\n\n\nTo serialize a queryset or list of objects instead of a single object instance, you should pass the \nmany=True\n flag when instantiating the serializer. You can then pass a queryset or list of objects to be serialized.\n\n\nqueryset = Book.objects.all()\nserializer = BookSerializer(queryset, many=True)\nserializer.data\n# [\n# {'id': 0, 'title': 'The electric kool-aid acid test', 'author': 'Tom Wolfe'},\n# {'id': 1, 'title': 'If this is a man', 'author': 'Primo Levi'},\n# {'id': 2, 'title': 'The wind-up bird chronicle', 'author': 'Haruki Murakami'}\n# ]\n\n\n\nDeserializing multiple objects\n\n\nThe default behavior for deserializing multiple objects is to support multiple object creation, but not support multiple object updates. For more information on how to support or customize either of these cases, see the \nListSerializer\n documentation below.\n\n\nIncluding extra context\n\n\nThere are some cases where you need to provide extra context to the serializer in addition to the object being serialized. One common case is if you're using a serializer that includes hyperlinked relations, which requires the serializer to have access to the current request so that it can properly generate fully qualified URLs.\n\n\nYou can provide arbitrary additional context by passing a \ncontext\n argument when instantiating the serializer. For example:\n\n\nserializer = AccountSerializer(account, context={'request': request})\nserializer.data\n# {'id': 6, 'owner': 'denvercoder9', 'created': datetime.datetime(2013, 2, 12, 09, 44, 56, 678870), 'details': 'http://example.com/accounts/6/details'}\n\n\n\nThe context dictionary can be used within any serializer field logic, such as a custom \n.to_representation()\n method, by accessing the \nself.context\n attribute.\n\n\n\n\nModelSerializer\n\n\nOften you'll want serializer classes that map closely to Django model definitions.\n\n\nThe \nModelSerializer\n class provides a shortcut that lets you automatically create a \nSerializer\n class with fields that correspond to the Model fields.\n\n\nThe \nModelSerializer\n class is the same as a regular \nSerializer\n class, except that\n:\n\n\n\n\nIt will automatically generate a set of fields for you, based on the model.\n\n\nIt will automatically generate validators for the serializer, such as unique_together validators.\n\n\nIt includes simple default implementations of \n.create()\n and \n.update()\n.\n\n\n\n\nDeclaring a \nModelSerializer\n looks like this:\n\n\nclass AccountSerializer(serializers.ModelSerializer):\n class Meta:\n model = Account\n fields = ('id', 'account_name', 'users', 'created')\n\n\n\nBy default, all the model fields on the class will be mapped to a corresponding serializer fields.\n\n\nAny relationships such as foreign keys on the model will be mapped to \nPrimaryKeyRelatedField\n. Reverse relationships are not included by default unless explicitly included as specified in the \nserializer relations\n documentation.\n\n\nInspecting a \nModelSerializer\n\n\nSerializer classes generate helpful verbose representation strings, that allow you to fully inspect the state of their fields. This is particularly useful when working with \nModelSerializers\n where you want to determine what set of fields and validators are being automatically created for you.\n\n\nTo do so, open the Django shell, using \npython manage.py shell\n, then import the serializer class, instantiate it, and print the object representation\u2026\n\n\n>>> from myapp.serializers import AccountSerializer\n>>> serializer = AccountSerializer()\n>>> print(repr(serializer))\nAccountSerializer():\n id = IntegerField(label='ID', read_only=True)\n name = CharField(allow_blank=True, max_length=100, required=False)\n owner = PrimaryKeyRelatedField(queryset=User.objects.all())\n\n\n\nSpecifying which fields to include\n\n\nIf you only want a subset of the default fields to be used in a model serializer, you can do so using \nfields\n or \nexclude\n options, just as you would with a \nModelForm\n. It is strongly recommended that you explicitly set all fields that should be serialized using the \nfields\n attribute. This will make it less likely to result in unintentionally exposing data when your models change.\n\n\nFor example:\n\n\nclass AccountSerializer(serializers.ModelSerializer):\n class Meta:\n model = Account\n fields = ('id', 'account_name', 'users', 'created')\n\n\n\nYou can also set the \nfields\n attribute to the special value \n'__all__'\n to indicate that all fields in the model should be used.\n\n\nFor example:\n\n\nclass AccountSerializer(serializers.ModelSerializer):\n class Meta:\n model = Account\n fields = '__all__'\n\n\n\nYou can set the \nexclude\n attribute to a list of fields to be excluded from the serializer.\n\n\nFor example:\n\n\nclass AccountSerializer(serializers.ModelSerializer):\n class Meta:\n model = Account\n exclude = ('users',)\n\n\n\nIn the example above, if the \nAccount\n model had 3 fields \naccount_name\n, \nusers\n, and \ncreated\n, this will result in the fields \naccount_name\n and \ncreated\n to be serialized.\n\n\nThe names in the \nfields\n and \nexclude\n attributes will normally map to model fields on the model class.\n\n\nAlternatively names in the \nfields\n options can map to properties or methods which take no arguments that exist on the model class.\n\n\nSince version 3.3.0, it is \nmandatory\n to provide one of the attributes \nfields\n or \nexclude\n.\n\n\nSpecifying nested serialization\n\n\nThe default \nModelSerializer\n uses primary keys for relationships, but you can also easily generate nested representations using the \ndepth\n option:\n\n\nclass AccountSerializer(serializers.ModelSerializer):\n class Meta:\n model = Account\n fields = ('id', 'account_name', 'users', 'created')\n depth = 1\n\n\n\nThe \ndepth\n option should be set to an integer value that indicates the depth of relationships that should be traversed before reverting to a flat representation.\n\n\nIf you want to customize the way the serialization is done you'll need to define the field yourself.\n\n\nSpecifying fields explicitly\n\n\nYou can add extra fields to a \nModelSerializer\n or override the default fields by declaring fields on the class, just as you would for a \nSerializer\n class.\n\n\nclass AccountSerializer(serializers.ModelSerializer):\n url = serializers.CharField(source='get_absolute_url', read_only=True)\n groups = serializers.PrimaryKeyRelatedField(many=True)\n\n class Meta:\n model = Account\n\n\n\nExtra fields can correspond to any property or callable on the model.\n\n\nSpecifying read only fields\n\n\nYou may wish to specify multiple fields as read-only. Instead of adding each field explicitly with the \nread_only=True\n attribute, you may use the shortcut Meta option, \nread_only_fields\n.\n\n\nThis option should be a list or tuple of field names, and is declared as follows:\n\n\nclass AccountSerializer(serializers.ModelSerializer):\n class Meta:\n model = Account\n fields = ('id', 'account_name', 'users', 'created')\n read_only_fields = ('account_name',)\n\n\n\nModel fields which have \neditable=False\n set, and \nAutoField\n fields will be set to read-only by default, and do not need to be added to the \nread_only_fields\n option.\n\n\n\n\nNote\n: There is a special-case where a read-only field is part of a \nunique_together\n constraint at the model level. In this case the field is required by the serializer class in order to validate the constraint, but should also not be editable by the user.\n\n\nThe right way to deal with this is to specify the field explicitly on the serializer, providing both the \nread_only=True\n and \ndefault=\u2026\n keyword arguments.\n\n\nOne example of this is a read-only relation to the currently authenticated \nUser\n which is \nunique_together\n with another identifier. In this case you would declare the user field like so:\n\n\nuser = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())\n\n\n\nPlease review the \nValidators Documentation\n for details on the \nUniqueTogetherValidator\n and \nCurrentUserDefault\n classes.\n\n\n\n\nAdditional keyword arguments\n\n\nThere is also a shortcut allowing you to specify arbitrary additional keyword arguments on fields, using the \nextra_kwargs\n option. As in the case of \nread_only_fields\n, this means you do not need to explicitly declare the field on the serializer.\n\n\nThis option is a dictionary, mapping field names to a dictionary of keyword arguments. For example:\n\n\nclass CreateUserSerializer(serializers.ModelSerializer):\n class Meta:\n model = User\n fields = ('email', 'username', 'password')\n extra_kwargs = {'password': {'write_only': True}}\n\n def create(self, validated_data):\n user = User(\n email=validated_data['email'],\n username=validated_data['username']\n )\n user.set_password(validated_data['password'])\n user.save()\n return user\n\n\n\nRelational fields\n\n\nWhen serializing model instances, there are a number of different ways you might choose to represent relationships. The default representation for \nModelSerializer\n is to use the primary keys of the related instances.\n\n\nAlternative representations include serializing using hyperlinks, serializing complete nested representations, or serializing with a custom representation.\n\n\nFor full details see the \nserializer relations\n documentation.\n\n\nCustomizing field mappings\n\n\nThe ModelSerializer class also exposes an API that you can override in order to alter how serializer fields are automatically determined when instantiating the serializer.\n\n\nNormally if a \nModelSerializer\n does not generate the fields you need by default then you should either add them to the class explicitly, or simply use a regular \nSerializer\n class instead. However in some cases you may want to create a new base class that defines how the serializer fields are created for any given model.\n\n\n.serializer_field_mapping\n\n\nA mapping of Django model classes to REST framework serializer classes. You can override this mapping to alter the default serializer classes that should be used for each model class.\n\n\n.serializer_related_field\n\n\nThis property should be the serializer field class, that is used for relational fields by default.\n\n\nFor \nModelSerializer\n this defaults to \nPrimaryKeyRelatedField\n.\n\n\nFor \nHyperlinkedModelSerializer\n this defaults to \nserializers.HyperlinkedRelatedField\n.\n\n\nserializer_url_field\n\n\nThe serializer field class that should be used for any \nurl\n field on the serializer.\n\n\nDefaults to \nserializers.HyperlinkedIdentityField\n\n\nserializer_choice_field\n\n\nThe serializer field class that should be used for any choice fields on the serializer.\n\n\nDefaults to \nserializers.ChoiceField\n\n\nThe field_class and field_kwargs API\n\n\nThe following methods are called to determine the class and keyword arguments for each field that should be automatically included on the serializer. Each of these methods should return a two tuple of \n(field_class, field_kwargs)\n.\n\n\n.build_standard_field(self, field_name, model_field)\n\n\nCalled to generate a serializer field that maps to a standard model field.\n\n\nThe default implementation returns a serializer class based on the \nserializer_field_mapping\n attribute.\n\n\n.build_relational_field(self, field_name, relation_info)\n\n\nCalled to generate a serializer field that maps to a relational model field.\n\n\nThe default implementation returns a serializer class based on the \nserializer_relational_field\n attribute.\n\n\nThe \nrelation_info\n argument is a named tuple, that contains \nmodel_field\n, \nrelated_model\n, \nto_many\n and \nhas_through_model\n properties.\n\n\n.build_nested_field(self, field_name, relation_info, nested_depth)\n\n\nCalled to generate a serializer field that maps to a relational model field, when the \ndepth\n option has been set.\n\n\nThe default implementation dynamically creates a nested serializer class based on either \nModelSerializer\n or \nHyperlinkedModelSerializer\n.\n\n\nThe \nnested_depth\n will be the value of the \ndepth\n option, minus one.\n\n\nThe \nrelation_info\n argument is a named tuple, that contains \nmodel_field\n, \nrelated_model\n, \nto_many\n and \nhas_through_model\n properties.\n\n\n.build_property_field(self, field_name, model_class)\n\n\nCalled to generate a serializer field that maps to a property or zero-argument method on the model class.\n\n\nThe default implementation returns a \nReadOnlyField\n class.\n\n\n.build_url_field(self, field_name, model_class)\n\n\nCalled to generate a serializer field for the serializer's own \nurl\n field. The default implementation returns a \nHyperlinkedIdentityField\n class.\n\n\n.build_unknown_field(self, field_name, model_class)\n\n\nCalled when the field name did not map to any model field or model property.\nThe default implementation raises an error, although subclasses may customize this behavior.\n\n\n\n\nHyperlinkedModelSerializer\n\n\nThe \nHyperlinkedModelSerializer\n class is similar to the \nModelSerializer\n class except that it uses hyperlinks to represent relationships, rather than primary keys.\n\n\nBy default the serializer will include a \nurl\n field instead of a primary key field.\n\n\nThe url field will be represented using a \nHyperlinkedIdentityField\n serializer field, and any relationships on the model will be represented using a \nHyperlinkedRelatedField\n serializer field.\n\n\nYou can explicitly include the primary key by adding it to the \nfields\n option, for example:\n\n\nclass AccountSerializer(serializers.HyperlinkedModelSerializer):\n class Meta:\n model = Account\n fields = ('url', 'id', 'account_name', 'users', 'created')\n\n\n\nAbsolute and relative URLs\n\n\nWhen instantiating a \nHyperlinkedModelSerializer\n you must include the current\n\nrequest\n in the serializer context, for example:\n\n\nserializer = AccountSerializer(queryset, context={'request': request})\n\n\n\nDoing so will ensure that the hyperlinks can include an appropriate hostname,\nso that the resulting representation uses fully qualified URLs, such as:\n\n\nhttp://api.example.com/accounts/1/\n\n\n\nRather than relative URLs, such as:\n\n\n/accounts/1/\n\n\n\nIf you \ndo\n want to use relative URLs, you should explicitly pass \n{'request': None}\n\nin the serializer context.\n\n\nHow hyperlinked views are determined\n\n\nThere needs to be a way of determining which views should be used for hyperlinking to model instances.\n\n\nBy default hyperlinks are expected to correspond to a view name that matches the style \n'{model_name}-detail'\n, and looks up the instance by a \npk\n keyword argument.\n\n\nYou can override a URL field view name and lookup field by using either, or both of, the \nview_name\n and \nlookup_field\n options in the \nextra_kwargs\n setting, like so:\n\n\nclass AccountSerializer(serializers.HyperlinkedModelSerializer):\n class Meta:\n model = Account\n fields = ('account_url', 'account_name', 'users', 'created')\n extra_kwargs = {\n 'url': {'view_name': 'accounts', 'lookup_field': 'account_name'},\n 'users': {'lookup_field': 'username'}\n }\n\n\n\nAlternatively you can set the fields on the serializer explicitly. For example:\n\n\nclass AccountSerializer(serializers.HyperlinkedModelSerializer):\n url = serializers.HyperlinkedIdentityField(\n view_name='accounts',\n lookup_field='slug'\n )\n users = serializers.HyperlinkedRelatedField(\n view_name='user-detail',\n lookup_field='username',\n many=True,\n read_only=True\n )\n\n class Meta:\n model = Account\n fields = ('url', 'account_name', 'users', 'created')\n\n\n\n\n\nTip\n: Properly matching together hyperlinked representations and your URL conf can sometimes be a bit fiddly. Printing the \nrepr\n of a \nHyperlinkedModelSerializer\n instance is a particularly useful way to inspect exactly which view names and lookup fields the relationships are expected to map too.\n\n\n\n\nChanging the URL field name\n\n\nThe name of the URL field defaults to 'url'. You can override this globally, by using the \nURL_FIELD_NAME\n setting.\n\n\n\n\nListSerializer\n\n\nThe \nListSerializer\n class provides the behavior for serializing and validating multiple objects at once. You won't \ntypically\n need to use \nListSerializer\n directly, but should instead simply pass \nmany=True\n when instantiating a serializer.\n\n\nWhen a serializer is instantiated and \nmany=True\n is passed, a \nListSerializer\n instance will be created. The serializer class then becomes a child of the parent \nListSerializer\n\n\nThe following argument can also be passed to a \nListSerializer\n field or a serializer that is passed \nmany=True\n:\n\n\nallow_empty\n\n\nThis is \nTrue\n by default, but can be set to \nFalse\n if you want to disallow empty lists as valid input.\n\n\nCustomizing \nListSerializer\n behavior\n\n\nThere \nare\n a few use cases when you might want to customize the \nListSerializer\n behavior. For example:\n\n\n\n\nYou want to provide particular validation of the lists, such as checking that one element does not conflict with another element in a list.\n\n\nYou want to customize the create or update behavior of multiple objects.\n\n\n\n\nFor these cases you can modify the class that is used when \nmany=True\n is passed, by using the \nlist_serializer_class\n option on the serializer \nMeta\n class.\n\n\nFor example:\n\n\nclass CustomListSerializer(serializers.ListSerializer):\n ...\n\nclass CustomSerializer(serializers.Serializer):\n ...\n class Meta:\n list_serializer_class = CustomListSerializer\n\n\n\nCustomizing multiple create\n\n\nThe default implementation for multiple object creation is to simply call \n.create()\n for each item in the list. If you want to customize this behavior, you'll need to customize the \n.create()\n method on \nListSerializer\n class that is used when \nmany=True\n is passed.\n\n\nFor example:\n\n\nclass BookListSerializer(serializers.ListSerializer):\n def create(self, validated_data):\n books = [Book(**item) for item in validated_data]\n return Book.objects.bulk_create(books)\n\nclass BookSerializer(serializers.Serializer):\n ...\n class Meta:\n list_serializer_class = BookListSerializer\n\n\n\nCustomizing multiple update\n\n\nBy default the \nListSerializer\n class does not support multiple updates. This is because the behavior that should be expected for insertions and deletions is ambiguous.\n\n\nTo support multiple updates you'll need to do so explicitly. When writing your multiple update code make sure to keep the following in mind:\n\n\n\n\nHow do you determine which instance should be updated for each item in the list of data?\n\n\nHow should insertions be handled? Are they invalid, or do they create new objects?\n\n\nHow should removals be handled? Do they imply object deletion, or removing a relationship? Should they be silently ignored, or are they invalid?\n\n\nHow should ordering be handled? Does changing the position of two items imply any state change or is it ignored?\n\n\n\n\nYou will need to add an explicit \nid\n field to the instance serializer. The default implicitly-generated \nid\n field is marked as \nread_only\n. This causes it to be removed on updates. Once you declare it explicitly, it will be available in the list serializer's \nupdate\n method.\n\n\nHere's an example of how you might choose to implement multiple updates:\n\n\nclass BookListSerializer(serializers.ListSerializer):\n def update(self, instance, validated_data):\n # Maps for id->instance and id->data item.\n book_mapping = {book.id: book for book in instance}\n data_mapping = {item['id']: item for item in validated_data}\n\n # Perform creations and updates.\n ret = []\n for book_id, data in data_mapping.items():\n book = book_mapping.get(book_id, None)\n if book is None:\n ret.append(self.child.create(data))\n else:\n ret.append(self.child.update(book, data))\n\n # Perform deletions.\n for book_id, book in book_mapping.items():\n if book_id not in data_mapping:\n book.delete()\n\n return ret\n\nclass BookSerializer(serializers.Serializer):\n # We need to identify elements in the list using their primary key,\n # so use a writable field here, rather than the default which would be read-only.\n id = serializers.IntegerField()\n ...\n\n class Meta:\n list_serializer_class = BookListSerializer\n\n\n\nIt is possible that a third party package may be included alongside the 3.1 release that provides some automatic support for multiple update operations, similar to the \nallow_add_remove\n behavior that was present in REST framework 2.\n\n\nCustomizing ListSerializer initialization\n\n\nWhen a serializer with \nmany=True\n is instantiated, we need to determine which arguments and keyword arguments should be passed to the \n.__init__()\n method for both the child \nSerializer\n class, and for the parent \nListSerializer\n class.\n\n\nThe default implementation is to pass all arguments to both classes, except for \nvalidators\n, and any custom keyword arguments, both of which are assumed to be intended for the child serializer class.\n\n\nOccasionally you might need to explicitly specify how the child and parent classes should be instantiated when \nmany=True\n is passed. You can do so by using the \nmany_init\n class method.\n\n\n @classmethod\n def many_init(cls, *args, **kwargs):\n # Instantiate the child serializer.\n kwargs['child'] = cls()\n # Instantiate the parent list serializer.\n return CustomListSerializer(*args, **kwargs)\n\n\n\n\n\nBaseSerializer\n\n\nBaseSerializer\n class that can be used to easily support alternative serialization and deserialization styles.\n\n\nThis class implements the same basic API as the \nSerializer\n class:\n\n\n\n\n.data\n - Returns the outgoing primitive representation.\n\n\n.is_valid()\n - Deserializes and validates incoming data.\n\n\n.validated_data\n - Returns the validated incoming data.\n\n\n.errors\n - Returns any errors during validation.\n\n\n.save()\n - Persists the validated data into an object instance.\n\n\n\n\nThere are four methods that can be overridden, depending on what functionality you want the serializer class to support:\n\n\n\n\n.to_representation()\n - Override this to support serialization, for read operations.\n\n\n.to_internal_value()\n - Override this to support deserialization, for write operations.\n\n\n.create()\n and \n.update()\n - Override either or both of these to support saving instances.\n\n\n\n\nBecause this class provides the same interface as the \nSerializer\n class, you can use it with the existing generic class-based views exactly as you would for a regular \nSerializer\n or \nModelSerializer\n.\n\n\nThe only difference you'll notice when doing so is the \nBaseSerializer\n 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.\n\n\nRead-only \nBaseSerializer\n classes\n\n\nTo implement a read-only serializer using the \nBaseSerializer\n class, we just need to override the \n.to_representation()\n method. Let's take a look at an example using a simple Django model:\n\n\nclass HighScore(models.Model):\n created = models.DateTimeField(auto_now_add=True)\n player_name = models.CharField(max_length=10)\n score = models.IntegerField()\n\n\n\nIt's simple to create a read-only serializer for converting \nHighScore\n instances into primitive data types.\n\n\nclass HighScoreSerializer(serializers.BaseSerializer):\n def to_representation(self, obj):\n return {\n 'score': obj.score,\n 'player_name': obj.player_name\n }\n\n\n\nWe can now use this class to serialize single \nHighScore\n instances:\n\n\n@api_view(['GET'])\ndef high_score(request, pk):\n instance = HighScore.objects.get(pk=pk)\n serializer = HighScoreSerializer(instance)\n return Response(serializer.data)\n\n\n\nOr use it to serialize multiple instances:\n\n\n@api_view(['GET'])\ndef all_high_scores(request):\n queryset = HighScore.objects.order_by('-score')\n serializer = HighScoreSerializer(queryset, many=True)\n return Response(serializer.data)\n\n\n\nRead-write \nBaseSerializer\n classes\n\n\nTo create a read-write serializer we first need to implement a \n.to_internal_value()\n method. This method returns the validated values that will be used to construct the object instance, and may raise a \nserializers.ValidationError\n if the supplied data is in an incorrect format.\n\n\nOnce you've implemented \n.to_internal_value()\n, the basic validation API will be available on the serializer, and you will be able to use \n.is_valid()\n, \n.validated_data\n and \n.errors\n.\n\n\nIf you want to also support \n.save()\n you'll need to also implement either or both of the \n.create()\n and \n.update()\n methods.\n\n\nHere's a complete example of our previous \nHighScoreSerializer\n, that's been updated to support both read and write operations.\n\n\nclass HighScoreSerializer(serializers.BaseSerializer):\n def to_internal_value(self, data):\n score = data.get('score')\n player_name = data.get('player_name')\n\n # Perform the data validation.\n if not score:\n raise serializers.ValidationError({\n 'score': 'This field is required.'\n })\n if not player_name:\n raise serializers.ValidationError({\n 'player_name': 'This field is required.'\n })\n if len(player_name) > 10:\n raise serializers.ValidationError({\n 'player_name': 'May not be more than 10 characters.'\n })\n\n # Return the validated values. This will be available as\n # the `.validated_data` property.\n return {\n 'score': int(score),\n 'player_name': player_name\n }\n\n def to_representation(self, obj):\n return {\n 'score': obj.score,\n 'player_name': obj.player_name\n }\n\n def create(self, validated_data):\n return HighScore.objects.create(**validated_data)\n\n\n\nCreating new base classes\n\n\nThe \nBaseSerializer\n class is also useful if you want to implement new generic serializer classes for dealing with particular serialization styles, or for integrating with alternative storage backends.\n\n\nThe following class is an example of a generic serializer that can handle coercing arbitrary objects into primitive representations.\n\n\nclass ObjectSerializer(serializers.BaseSerializer):\n \"\"\"\n A read-only serializer that coerces arbitrary complex objects\n into primitive representations.\n \"\"\"\n def to_representation(self, obj):\n for attribute_name in dir(obj):\n attribute = getattr(obj, attribute_name)\n if attribute_name('_'):\n # Ignore private attributes.\n pass\n elif hasattr(attribute, '__call__'):\n # Ignore methods and other callables.\n pass\n elif isinstance(attribute, (str, int, bool, float, type(None))):\n # Primitive types can be passed through unmodified.\n output[attribute_name] = attribute\n elif isinstance(attribute, list):\n # Recursively deal with items in lists.\n output[attribute_name] = [\n self.to_representation(item) for item in attribute\n ]\n elif isinstance(attribute, dict):\n # Recursively deal with items in dictionaries.\n output[attribute_name] = {\n str(key): self.to_representation(value)\n for key, value in attribute.items()\n }\n else:\n # Force anything else to its string representation.\n output[attribute_name] = str(attribute)\n\n\n\n\n\nAdvanced serializer usage\n\n\nOverriding serialization and deserialization behavior\n\n\nIf you need to alter the serialization or deserialization behavior of a serializer class, you can do so by overriding the \n.to_representation()\n or \n.to_internal_value()\n methods.\n\n\nSome reasons this might be useful include...\n\n\n\n\nAdding new behavior for new serializer base classes.\n\n\nModifying the behavior slightly for an existing class.\n\n\nImproving serialization performance for a frequently accessed API endpoint that returns lots of data.\n\n\n\n\nThe signatures for these methods are as follows:\n\n\n.to_representation(self, obj)\n\n\nTakes the object instance that requires serialization, and should return a primitive representation. Typically this means returning a structure of built-in Python datatypes. The exact types that can be handled will depend on the render classes you have configured for your API.\n\n\nMay be overridden in order modify the representation style. For example:\n\n\ndef to_representation(self, instance):\n \"\"\"Convert `username` to lowercase.\"\"\"\n ret = super().to_representation(instance)\n ret['username'] = ret['username'].lower()\n return ret\n\n\n\n.to_internal_value(self, data)\n\n\nTakes the unvalidated incoming data as input and should return the validated data that will be made available as \nserializer.validated_data\n. The return value will also be passed to the \n.create()\n or \n.update()\n methods if \n.save()\n is called on the serializer class.\n\n\nIf any of the validation fails, then the method should raise a \nserializers.ValidationError(errors)\n. The \nerrors\n argument should be a dictionary mapping field names (or \nsettings.NON_FIELD_ERRORS_KEY\n) to a list of error messages. If you don't need to alter deserialization behavior and instead want to provide object-level validation, it's recommended that you instead override the \n.validate()\n method.\n\n\nThe \ndata\n argument passed to this method will normally be the value of \nrequest.data\n, so the datatype it provides will depend on the parser classes you have configured for your API.\n\n\nSerializer Inheritance\n\n\nSimilar 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,\n\n\nclass MyBaseSerializer(Serializer):\n my_field = serializers.CharField()\n\n def validate_my_field(self, value):\n ...\n\nclass MySerializer(MyBaseSerializer):\n ...\n\n\n\nLike Django's \nModel\n and \nModelForm\n classes, the inner \nMeta\n class on serializers does not implicitly inherit from it's parents' inner \nMeta\n classes. If you want the \nMeta\n class to inherit from a parent class you must do so explicitly. For example:\n\n\nclass AccountSerializer(MyBaseSerializer):\n class Meta(MyBaseSerializer.Meta):\n model = Account\n\n\n\nTypically we would recommend \nnot\n using inheritance on inner Meta classes, but instead declaring all options explicitly.\n\n\nAdditionally, the following caveats apply to serializer inheritance:\n\n\n\n\nNormal Python name resolution rules apply. If you have multiple base classes that declare a \nMeta\n inner class, only the first one will be used. This means the child\u2019s \nMeta\n, if it exists, otherwise the \nMeta\n of the first parent, etc.\n\n\n\n\nIt\u2019s possible to declaratively remove a \nField\n inherited from a parent class by setting the name to be \nNone\n on the subclass.\n\n\nclass MyBaseSerializer(ModelSerializer):\n my_field = serializers.CharField()\n\nclass MySerializer(MyBaseSerializer):\n my_field = None\n\n\n\nHowever, you can only use this technique to opt out from a field defined declaratively by a parent class; it won\u2019t prevent the \nModelSerializer\n from generating a default field. To opt-out from default fields, see \nSpecifying which fields to include\n.\n\n\n\n\n\n\nDynamically modifying fields\n\n\nOnce a serializer has been initialized, the dictionary of fields that are set on the serializer may be accessed using the \n.fields\n attribute. Accessing and modifying this attribute allows you to dynamically modify the serializer.\n\n\nModifying the \nfields\n argument directly allows you to do interesting things such as changing the arguments on serializer fields at runtime, rather than at the point of declaring the serializer.\n\n\nExample\n\n\nFor example, if you wanted to be able to set which fields should be used by a serializer at the point of initializing it, you could create a serializer class like so:\n\n\nclass DynamicFieldsModelSerializer(serializers.ModelSerializer):\n \"\"\"\n A ModelSerializer that takes an additional `fields` argument that\n controls which fields should be displayed.\n \"\"\"\n\n def __init__(self, *args, **kwargs):\n # Don't pass the 'fields' arg up to the superclass\n fields = kwargs.pop('fields', None)\n\n # Instantiate the superclass normally\n super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)\n\n if fields is not None:\n # Drop any fields that are not specified in the `fields` argument.\n allowed = set(fields)\n existing = set(self.fields)\n for field_name in existing - allowed:\n self.fields.pop(field_name)\n\n\n\nThis would then allow you to do the following:\n\n\n>>> class UserSerializer(DynamicFieldsModelSerializer):\n>>> class Meta:\n>>> model = User\n>>> fields = ('id', 'username', 'email')\n>>>\n>>> print(UserSerializer(user))\n{'id': 2, 'username': 'jonwatts', 'email': 'jon@example.com'}\n>>>\n>>> print(UserSerializer(user, fields=('id', 'email')))\n{'id': 2, 'email': 'jon@example.com'}\n\n\n\nCustomizing the default fields\n\n\nREST framework 2 provided an API to allow developers to override how a \nModelSerializer\n class would automatically generate the default set of fields.\n\n\nThis API included the \n.get_field()\n, \n.get_pk_field()\n and other methods.\n\n\nBecause the serializers have been fundamentally redesigned with 3.0 this API no longer exists. You can still modify the fields that get created but you'll need to refer to the source code, and be aware that if the changes you make are against private bits of API then they may be subject to change.\n\n\n\n\nThird party packages\n\n\nThe following third party packages are also available.\n\n\nDjango REST marshmallow\n\n\nThe \ndjango-rest-marshmallow\n package provides an alternative implementation for serializers, using the python \nmarshmallow\n library. It exposes the same API as the REST framework serializers, and can be used as a drop-in replacement in some use-cases.\n\n\nSerpy\n\n\nThe \nserpy\n package is an alternative implementation for serializers that is built for speed. \nSerpy\n serializes complex datatypes to simple native types. The native types can be easily converted to JSON or any other format needed.\n\n\nMongoengineModelSerializer\n\n\nThe \ndjango-rest-framework-mongoengine\n package provides a \nMongoEngineModelSerializer\n serializer class that supports using MongoDB as the storage layer for Django REST framework.\n\n\nGeoFeatureModelSerializer\n\n\nThe \ndjango-rest-framework-gis\n package provides a \nGeoFeatureModelSerializer\n serializer class that supports GeoJSON both for read and write operations.\n\n\nHStoreSerializer\n\n\nThe \ndjango-rest-framework-hstore\n package provides an \nHStoreSerializer\n to support \ndjango-hstore\n \nDictionaryField\n model field and its \nschema-mode\n feature.\n\n\nDynamic REST\n\n\nThe \ndynamic-rest\n 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.\n\n\nDynamic Fields Mixin\n\n\nThe \ndrf-dynamic-fields\n package provides a mixin to dynamically limit the fields per serializer to a subset specified by an URL parameter.\n\n\nDRF FlexFields\n\n\nThe \ndrf-flex-fields\n 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.\n\n\nSerializer Extensions\n\n\nThe \ndjango-rest-framework-serializer-extensions\n\npackage provides a collection of tools to DRY up your serializers, by allowing\nfields to be defined on a per-view/request basis. Fields can be whitelisted,\nblacklisted and child serializers can be optionally expanded.\n\n\nHTML JSON Forms\n\n\nThe \nhtml-json-forms\n package provides an algorithm and serializer for processing \n