From 25f74c45cefb25a521cbfa941031e1764be18c55 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 5 Sep 2012 13:05:36 +0100 Subject: [PATCH] Latest build --- api-guide/authentication.html | 223 +++++++++++++++++ api-guide/contentnegotiation.html | 4 +- api-guide/exceptions.html | 4 +- api-guide/parsers.html | 4 +- api-guide/permissions.html | 153 ++++++++++++ api-guide/renderers.html | 4 +- api-guide/requests.html | 4 +- api-guide/responses.html | 4 +- api-guide/{urls.html => reverse.html} | 4 +- api-guide/serializers.html | 4 +- api-guide/settings.html | 228 ++++++++++++++++++ api-guide/status-codes.html | 4 +- api-guide/throttling.html | 153 ++++++++++++ api-guide/views.html | 4 +- index.html | 13 +- topics/contributing.html | 162 +++++++++++++ topics/credits.html | 4 +- topics/csrf.html | 4 +- topics/formoverloading.html | 4 +- tutorial/1-serialization.html | 14 +- tutorial/2-requests-and-responses.html | 37 +-- tutorial/3-class-based-views.html | 78 +++--- ...entication-permissions-and-throttling.html | 4 +- .../5-relationships-and-hyperlinked-apis.html | 4 +- tutorial/6-resource-orientated-projects.html | 4 +- 25 files changed, 1046 insertions(+), 79 deletions(-) create mode 100644 api-guide/authentication.html create mode 100644 api-guide/permissions.html rename api-guide/{urls.html => reverse.html} (96%) create mode 100644 api-guide/settings.html create mode 100644 api-guide/throttling.html create mode 100644 topics/contributing.html diff --git a/api-guide/authentication.html b/api-guide/authentication.html new file mode 100644 index 000000000..3f1b9244f --- /dev/null +++ b/api-guide/authentication.html @@ -0,0 +1,223 @@ + + + + Django REST framework + + + + + + + + + + + + + + + +
+
+ + +
+

Authentication

+

Authentication is the mechanism of associating an incoming request with a set of identifying credentials, such as the user the request came from, or the token that it was signed with. The permission and throttling policies can then use those credentials to determine if the request should be permitted.

+

REST framework provides a number of authentication policies out of the box, and also allows you to implement custom policies.

+

Authentication will run the first time either the request.user or request.auth properties are accessed, and determines how those properties are initialized.

+

Setting the authentication policy

+

The default authentication policy may be set globally, using the DEFAULT_AUTHENTICATION_CLASSES setting. For example.

+
API_SETTINGS = {
+    'DEFAULT_AUTHENTICATION_CLASSES': (
+        'djangorestframework.authentication.SessionAuthentication',
+    )
+}
+
+

You can also set the authentication policy on a per-view basis, using the APIView class based views.

+
class ExampleView(APIView):
+    authentication_classes = (SessionAuthentication,)
+
+    def get(self, request, format=None):
+        content = {
+            'user': unicode(request.user),  # `django.contrib.auth.User` instance. 
+            'auth': unicode(request.auth),  # None
+        }
+        return Response(content)
+
+

Or, if you're using the @api_view decorator with function based views.

+
@api_view(allowed=('GET',), authentication_classes=(SessionAuthentication,))
+def example_view(request, format=None):
+    content = {
+        'user': unicode(request.user),  # `django.contrib.auth.User` instance. 
+        'auth': unicode(request.auth),  # None
+    }
+    return Response(content)
+
+

UserBasicAuthentication

+

This policy uses HTTP Basic Authentication, signed against a user's username and password. User basic authentication is generally only appropriate for testing.

+

Note: If you run UserBasicAuthentication in production your API must be https only, or it will be completely insecure. You should also ensure that your API clients will always re-request the username and password at login, and will never store those details to persistent storage.

+

If successfully authenticated, UserBasicAuthentication provides the following credentials.

+
    +
  • request.user will be a django.contrib.auth.models.User instance.
  • +
  • request.auth will be None.
  • +
+

TokenBasicAuthentication

+

This policy uses HTTP Basic Authentication, signed against a token key and secret. Token basic authentication is appropriate for client-server setups, such as native desktop and mobile clients.

+

Note: If you run TokenBasicAuthentication in production your API must be https only, or it will be completely insecure.

+

If successfully authenticated, TokenBasicAuthentication provides the following credentials.

+
    +
  • request.user will be a django.contrib.auth.models.User instance.
  • +
  • request.auth will be a djangorestframework.models.BasicToken instance.
  • +
+

OAuthAuthentication

+

This policy uses the OAuth 2.0 protocol to authenticate requests. OAuth is appropriate for server-server setups, such as when you want to allow a third-party service to access your API on a user's behalf.

+

If successfully authenticated, OAuthAuthentication provides the following credentials.

+
    +
  • request.user will be a django.contrib.auth.models.User instance.
  • +
  • request.auth will be a djangorestframework.models.OAuthToken instance.
  • +
+

SessionAuthentication

+

This policy uses Django's default session backend for authentication. Session authentication is appropriate for AJAX clients that are running in the same session context as your website.

+

If successfully authenticated, SessionAuthentication provides the following credentials.

+
    +
  • request.user will be a django.contrib.auth.models.User instance.
  • +
  • request.auth will be None.
  • +
+

Custom authentication policies

+

To implement a custom authentication policy, subclass BaseAuthentication and override the authenticate(self, request) method. The method should return a two-tuple of (user, auth) if authentication succeeds, or None otherwise.

+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/api-guide/contentnegotiation.html b/api-guide/contentnegotiation.html index de478f1c3..5e58ed8dc 100644 --- a/api-guide/contentnegotiation.html +++ b/api-guide/contentnegotiation.html @@ -94,7 +94,8 @@ margin-top: 5px;
  • Throttling
  • Exceptions
  • Status codes
  • -
  • Returning URLs
  • +
  • Returning URLs
  • +
  • Settings
  • diff --git a/api-guide/exceptions.html b/api-guide/exceptions.html index 21e6e3586..8dc70117c 100644 --- a/api-guide/exceptions.html +++ b/api-guide/exceptions.html @@ -94,7 +94,8 @@ margin-top: 5px;
  • Throttling
  • Exceptions
  • Status codes
  • -
  • Returning URLs
  • +
  • Returning URLs
  • +
  • Settings
  • diff --git a/api-guide/parsers.html b/api-guide/parsers.html index 62604ad99..909f3e892 100644 --- a/api-guide/parsers.html +++ b/api-guide/parsers.html @@ -94,7 +94,8 @@ margin-top: 5px;
  • Throttling
  • Exceptions
  • Status codes
  • -
  • Returning URLs
  • +
  • Returning URLs
  • +
  • Settings
  • diff --git a/api-guide/permissions.html b/api-guide/permissions.html new file mode 100644 index 000000000..f69d5c1a2 --- /dev/null +++ b/api-guide/permissions.html @@ -0,0 +1,153 @@ + + + + Django REST framework + + + + + + + + + + + + + + + +
    +
    +
    +
    + +
    +
    + +
    + +
    +
    +
    + + + + + + + + \ No newline at end of file diff --git a/api-guide/renderers.html b/api-guide/renderers.html index f727931e8..45e023295 100644 --- a/api-guide/renderers.html +++ b/api-guide/renderers.html @@ -94,7 +94,8 @@ margin-top: 5px;
  • Throttling
  • Exceptions
  • Status codes
  • -
  • Returning URLs
  • +
  • Returning URLs
  • +
  • Settings
  • diff --git a/api-guide/requests.html b/api-guide/requests.html index c757b9998..a4fb3e359 100644 --- a/api-guide/requests.html +++ b/api-guide/requests.html @@ -94,7 +94,8 @@ margin-top: 5px;
  • Throttling
  • Exceptions
  • Status codes
  • -
  • Returning URLs
  • +
  • Returning URLs
  • +
  • Settings
  • diff --git a/api-guide/responses.html b/api-guide/responses.html index ee0d42867..d5bdddf9a 100644 --- a/api-guide/responses.html +++ b/api-guide/responses.html @@ -94,7 +94,8 @@ margin-top: 5px;
  • Throttling
  • Exceptions
  • Status codes
  • -
  • Returning URLs
  • +
  • Returning URLs
  • +
  • Settings
  • diff --git a/api-guide/urls.html b/api-guide/reverse.html similarity index 96% rename from api-guide/urls.html rename to api-guide/reverse.html index 87d278caf..057a3b94e 100644 --- a/api-guide/urls.html +++ b/api-guide/reverse.html @@ -94,7 +94,8 @@ margin-top: 5px;
  • Throttling
  • Exceptions
  • Status codes
  • -
  • Returning URLs
  • +
  • Returning URLs
  • +
  • Settings
  • diff --git a/api-guide/serializers.html b/api-guide/serializers.html index 88d617b38..15311e513 100644 --- a/api-guide/serializers.html +++ b/api-guide/serializers.html @@ -94,7 +94,8 @@ margin-top: 5px;
  • Throttling
  • Exceptions
  • Status codes
  • -
  • Returning URLs
  • +
  • Returning URLs
  • +
  • Settings
  • diff --git a/api-guide/settings.html b/api-guide/settings.html new file mode 100644 index 000000000..08a709ac1 --- /dev/null +++ b/api-guide/settings.html @@ -0,0 +1,228 @@ + + + + Django REST framework + + + + + + + + + + + + + + + +
    +
    + + +
    +

    Settings

    +

    Settings for REST framework are all namespaced in the API_SETTINGS setting. +For example your project's settings.py file might look like this:

    +
    API_SETTINGS = {
    +    'DEFAULT_RENDERERS': (
    +        'djangorestframework.renderers.YAMLRenderer',
    +    )
    +    'DEFAULT_PARSERS': (
    +        'djangorestframework.parsers.YAMLParser',
    +    )
    +}
    +
    +

    DEFAULT_RENDERERS

    +

    A list or tuple of renderer classes, that determines the default set of renderers that may be used when returning a Response object.

    +

    Default:

    +
    (
    +    'djangorestframework.renderers.JSONRenderer',
    +    'djangorestframework.renderers.DocumentingHTMLRenderer'
    +    'djangorestframework.renderers.TemplateHTMLRenderer'
    +)
    +
    +

    DEFAULT_PARSERS

    +

    A list or tuple of parser classes, that determines the default set of parsers used when accessing the request.DATA property.

    +

    Default:

    +
    (
    +    'djangorestframework.parsers.JSONParser',
    +    'djangorestframework.parsers.FormParser'
    +)
    +
    +

    DEFAULT_AUTHENTICATION

    +

    A list or tuple of authentication classes, that determines the default set of authenticators used when accessing the request.user or request.auth properties.

    +

    Default if DEBUG is True:

    +
    (
    +    'djangorestframework.authentication.SessionAuthentication',
    +    'djangorestframework.authentication.UserBasicAuthentication'
    +)
    +
    +

    Default if DEBUG is False:

    +
    (
    +    'djangorestframework.authentication.SessionAuthentication',
    +)
    +
    +

    DEFAULT_PERMISSIONS

    +

    Default: ()

    +

    DEFAULT_THROTTLES

    +

    Default: ()

    +

    DEFAULT_MODEL_SERIALIZER

    +

    Default: djangorestframework.serializers.ModelSerializer

    +

    DEFAULT_PAGINATION_SERIALIZER

    +

    Default: djangorestframework.pagination.PaginationSerializer

    +

    FORMAT_SUFFIX_KWARG

    +

    Default: format

    +

    UNAUTHENTICATED_USER_CLASS

    +

    Default: django.contrib.auth.models.AnonymousUser

    +

    FORM_METHOD_OVERRIDE

    +

    Default: _method

    +

    FORM_CONTENT_OVERRIDE

    +

    Default: _content

    +

    FORM_CONTENTTYPE_OVERRIDE

    +

    Default: _content_type

    +

    URL_ACCEPT_OVERRIDE

    +

    Default: _accept

    +
    +
    +
    + + + + + + + + \ No newline at end of file diff --git a/api-guide/status-codes.html b/api-guide/status-codes.html index 33e37e98d..97320e342 100644 --- a/api-guide/status-codes.html +++ b/api-guide/status-codes.html @@ -94,7 +94,8 @@ margin-top: 5px;
  • Throttling
  • Exceptions
  • Status codes
  • -
  • Returning URLs
  • +
  • Returning URLs
  • +
  • Settings
  • diff --git a/api-guide/throttling.html b/api-guide/throttling.html new file mode 100644 index 000000000..f69d5c1a2 --- /dev/null +++ b/api-guide/throttling.html @@ -0,0 +1,153 @@ + + + + Django REST framework + + + + + + + + + + + + + + + +
    +
    +
    +
    + +
    +
    + +
    + +
    +
    +
    + + + + + + + + \ No newline at end of file diff --git a/api-guide/views.html b/api-guide/views.html index 869a11a7b..dd7cb9c0b 100644 --- a/api-guide/views.html +++ b/api-guide/views.html @@ -94,7 +94,8 @@ margin-top: 5px;
  • Throttling
  • Exceptions
  • Status codes
  • -
  • Returning URLs
  • +
  • Returning URLs
  • +
  • Settings
  • diff --git a/index.html b/index.html index c24a87103..5fe958f6e 100644 --- a/index.html +++ b/index.html @@ -94,7 +94,8 @@ margin-top: 5px;
  • Throttling
  • Exceptions
  • Status codes
  • -
  • Returning URLs
  • +
  • Returning URLs
  • +
  • Settings
  • @@ -179,7 +181,7 @@ pip install -r optionals.txt

    If you're intending to use the browserable API you'll want to add REST framework's login and logout views. Add the following to your root urls.py file.

    urlpatterns = patterns('',
         ...
    -    url(r'^auth', include('djangorestframework.urls', namespace='djangorestframework'))
    +    url(r'^api-auth/', include('djangorestframework.urls', namespace='djangorestframework'))
     )
     

    Quickstart

    @@ -205,15 +207,18 @@ pip install -r optionals.txt
  • Serializers
  • Authentication
  • Permissions
  • +
  • Throttling
  • Exceptions
  • -
  • Status codes
  • -
  • Returning URLs
  • +
  • Status codes
  • +
  • Returning URLs
  • +
  • Settings
  • Topics

    General guides to using REST framework.

    Development

    diff --git a/topics/contributing.html b/topics/contributing.html new file mode 100644 index 000000000..69ea6a80c --- /dev/null +++ b/topics/contributing.html @@ -0,0 +1,162 @@ + + + + Django REST framework + + + + + + + + + + + + + + + +
    +
    + + +
    +

    Contributing to REST framework

    +

    Accessing settings

    +

    Describe api_settings

    +

    Managing compatibility issues

    +

    Describe compat module

    +

    Running the tests

    +
    +
    +
    + + + + + + + + \ No newline at end of file diff --git a/topics/credits.html b/topics/credits.html index 039816681..64b62ef67 100644 --- a/topics/credits.html +++ b/topics/credits.html @@ -94,7 +94,8 @@ margin-top: 5px;
  • Throttling
  • Exceptions
  • Status codes
  • -
  • Returning URLs
  • +
  • Returning URLs
  • +
  • Settings
  • diff --git a/topics/csrf.html b/topics/csrf.html index 9b99e7c6f..fe80dc9de 100644 --- a/topics/csrf.html +++ b/topics/csrf.html @@ -94,7 +94,8 @@ margin-top: 5px;
  • Throttling
  • Exceptions
  • Status codes
  • -
  • Returning URLs
  • +
  • Returning URLs
  • +
  • Settings
  • diff --git a/topics/formoverloading.html b/topics/formoverloading.html index 040f24a5d..da9554529 100644 --- a/topics/formoverloading.html +++ b/topics/formoverloading.html @@ -94,7 +94,8 @@ margin-top: 5px;
  • Throttling
  • Exceptions
  • Status codes
  • -
  • Returning URLs
  • +
  • Returning URLs
  • +
  • Settings
  • diff --git a/tutorial/1-serialization.html b/tutorial/1-serialization.html index 7fb9e36fd..d182060e6 100644 --- a/tutorial/1-serialization.html +++ b/tutorial/1-serialization.html @@ -94,7 +94,8 @@ margin-top: 5px;
  • Throttling
  • Exceptions
  • Status codes
  • -
  • Returning URLs
  • +
  • Returning URLs
  • +
  • Settings
  • @@ -147,14 +149,14 @@ margin-top: 5px;

    Setting up a new environment

    Before we do anything else we'll create a new virtual environment, using virtualenv. This will make sure our package configuration is keep nicely isolated from any other projects we're working on.

    mkdir ~/env
    -virtualenv --no-site-packages ~/env/djangorestframework
    -source ~/env/djangorestframework/bin/activate
    +virtualenv --no-site-packages ~/env/tutorial
    +source ~/env/tutorial/bin/activate
     

    Now that we're inside a virtualenv environment, we can install our package requirements.

    pip install django
     pip install djangorestframework
     
    -

    *Note: To exit the virtualenv environment at any time, just type deactivate. For more information see the virtualenv documentation.*

    +

    Note: To exit the virtualenv environment at any time, just type deactivate. For more information see the virtualenv documentation.

    Getting started

    Okay, we're ready to get coding. To get started, let's create a new project to work with.

    @@ -245,7 +247,7 @@ c3.save()

    We've now got a few comment instances to play with. Let's take a look at serializing one of those instances.

    serializer = CommentSerializer(instance=c1)
     serializer.data
    -# {'email': u'leila@example.com', 'content': u'nothing to say', 'created': datetime.datetime(2012, 8, 22, 16, 20, 9, 822774)}
    +# {'email': u'leila@example.com', 'content': u'nothing to say', 'created': datetime.datetime(2012, 8, 22, 16, 20, 9, 822774, tzinfo=<UTC>)}
     

    At this point we've translated the model instance into python native datatypes. To finalise the serialization process we render the data into json.

    stream = JSONRenderer().render(serializer.data)
    @@ -260,7 +262,7 @@ stream
     serializer.is_valid()
     # True
     serializer.object
    -# <Comment object at 0x10633b2d0>
    +# <Comment: Comment 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.

    Writing regular Django views using our Serializers

    diff --git a/tutorial/2-requests-and-responses.html b/tutorial/2-requests-and-responses.html index f1a05b259..80a04b157 100644 --- a/tutorial/2-requests-and-responses.html +++ b/tutorial/2-requests-and-responses.html @@ -94,7 +94,8 @@ margin-top: 5px;
  • Throttling
  • Exceptions
  • Status codes
  • -
  • Returning URLs
  • +
  • Returning URLs
  • +
  • Settings
  • @@ -164,14 +166,18 @@ request.DATA # Handles arbitrary data. Works any HTTP request with content.

    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.

    Pulling it all together

    Okay, let's go ahead and start using these new components to write a few views.

    -
    from djangorestframework.decorators import api_view
    -from djangorestframework.status import *
    +

    We don't need our JSONResponse class anymore, so go ahead and delete that. Once that's done we can start refactoring our views slightly.

    +
    from blog.models import Comment
    +from blog.serializers import CommentSerializer
    +from djangorestframework import status
    +from djangorestframework.decorators import api_view
    +from djangorestframework.response import Response
     
    -@api_view(allow=['GET', 'POST'])
    +@api_view(['GET', 'POST'])
     def comment_root(request):
         """
         List all comments, or create a new comment.
    -    """ 
    +    """
         if request.method == 'GET':
             comments = Comment.objects.all()
             serializer = CommentSerializer(instance=comments)
    @@ -182,12 +188,12 @@ def comment_root(request):
             if serializer.is_valid():
                 comment = serializer.object
                 comment.save()
    -            return Response(serializer.data, status=HTTP_201_CREATED)
    +            return Response(serializer.data, status=status.HTTP_201_CREATED)
             else:
    -            return Response(serializer.error_data, status=HTTP_400_BAD_REQUEST)
    +            return Response(serializer.error_data, status=status.HTTP_400_BAD_REQUEST)
     
    -

    Our instance view is an improvement over the previous example. It's slightly more concise, and the code now feels very similar to if we were working with the Forms API.

    -
    @api_view(allow=['GET', 'PUT', 'DELETE'])
    +

    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.

    +
    @api_view(['GET', 'PUT', 'DELETE'])
     def comment_instance(request, pk):
         """
         Retrieve, update or delete a comment instance.
    @@ -195,7 +201,7 @@ def comment_instance(request, pk):
         try:
             comment = Comment.objects.get(pk=pk)
         except Comment.DoesNotExist:
    -        return Response(status=HTTP_404_NOT_FOUND)
    +        return Response(status=status.HTTP_404_NOT_FOUND)
     
         if request.method == 'GET':
             serializer = CommentSerializer(instance=comment)
    @@ -208,16 +214,16 @@ def comment_instance(request, pk):
                 comment.save()
                 return Response(serializer.data)
             else:
    -            return Response(serializer.error_data, status=HTTP_400_BAD_REQUEST)
    +            return Response(serializer.error_data, status=status.HTTP_400_BAD_REQUEST)
     
         elif request.method == 'DELETE':
             comment.delete()
    -        return Response(status=HTTP_204_NO_CONTENT)
    +        return Response(status=status.HTTP_204_NO_CONTENT)
     
    -

    This should all feel very familiar - it looks a lot like working with forms in regular Django views.

    +

    This should all feel very familiar - there's not a lot different to 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 yaml and 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.

    Adding optional format suffixes to our URLs

    -

    To take advantage of that, let's add support for format suffixes to our API endpoints, so that we can use URLs that explicitly refer to a given format. That means our API will be able to handle URLs such as http://example.com/api/items/4.json.

    +

    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 comment_root(request, format=None):
     
    @@ -225,7 +231,8 @@ def comment_instance(request, pk):
    def comment_instance(request, pk, format=None):
     

    Now update the urls.py file slightly, to append a set of format_suffix_patterns in addition to the existing URLs.

    -
    from djangorestframework.urlpatterns import format_suffix_patterns
    +
    from django.conf.urls import patterns, url
    +from djangorestframework.urlpatterns import format_suffix_patterns
     
     urlpatterns = patterns('blogpost.views',
         url(r'^$', 'comment_root'),
    diff --git a/tutorial/3-class-based-views.html b/tutorial/3-class-based-views.html
    index e5c774d82..b48dcade6 100644
    --- a/tutorial/3-class-based-views.html
    +++ b/tutorial/3-class-based-views.html
    @@ -94,7 +94,8 @@ margin-top: 5px;
                       
  • Throttling
  • Exceptions
  • Status codes
  • -
  • Returning URLs
  • +
  • Returning URLs
  • +
  • Settings
  • @@ -144,9 +146,9 @@ from blog.serializers import ComentSerializer from django.http import Http404 from djangorestframework.views import APIView from djangorestframework.response import Response -from djangorestframework.status import * +from djangorestframework.status import status -class CommentRoot(views.APIView): +class CommentRoot(APIView): """ List all comments, or create a new comment. """ @@ -161,19 +163,20 @@ class CommentRoot(views.APIView): comment = serializer.object comment.save() return Response(serializer.serialized, status=HTTP_201_CREATED) - else: - return Response(serializer.serialized_errors, status=HTTP_400_BAD_REQUEST) + return Response(serializer.serialized_errors, status=HTTP_400_BAD_REQUEST) + + comment_root = CommentRoot.as_view()

    So far, so good. It looks pretty similar to the previous case, but we've got better seperation between the different HTTP methods. We'll also need to update the instance view.

    -
    class CommentInstance(views.APIView):
    +
    class CommentInstance(APIView):
         """
         Retrieve, update or delete a comment instance.
         """
     
         def get_object(self, pk):
             try:
    -            return Poll.objects.get(pk=pk)
    -        except Poll.DoesNotExist:
    +            return Comment.objects.get(pk=pk)
    +        except Comment.DoesNotExist:
                 raise Http404
     
         def get(self, request, pk, format=None):
    @@ -188,54 +191,53 @@ class CommentRoot(views.APIView):
                 comment = serializer.deserialized
                 comment.save()
                 return Response(serializer.data)
    -        else:
    -            return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
    +        return Response(serializer.error_data, status=status.HTTP_400_BAD_REQUEST)
     
         def delete(self, request, pk, format=None):
             comment = self.get_object(pk)
             comment.delete()
    -        return Response(status=HTTP_204_NO_CONTENT)
    -
    -

    That's looking good. Again, it's still pretty similar to the function based view right now.

    -

    Since we're now working with class based views, rather than function based views, we'll also need to update our urlconf slightly.

    -
    from blogpost import views
    -from djangorestframework.urlpatterns import format_suffix_patterns
    +        return Response(status=status.HTTP_204_NO_CONTENT)
     
    -urlpatterns = patterns('',
    -    url(r'^$', views.CommentRoot.as_view()),
    -    url(r'^(?P<id>[0-9]+)$', views.CommentInstance.as_view())
    -)
    -
    -urlpatterns = format_suffix_patterns(urlpatterns)
    +    comment_instance = CommentInstance.as_view()
     
    -

    Okay, we're done. If you run the development server everything should be working just as before.

    +

    That's looking good. Again, it's still pretty similar to the function based view right now. +Okay, we're done. If you run the development server everything should be working just as before.

    Using mixins

    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 is going to be pretty simliar for any model-backed API views we create. Those bits of common behaviour are implemented in REST framework's mixin classes.

    We can compose those mixin classes, to recreate our existing API behaviour with less code.

    from blog.models import Comment
     from blog.serializers import CommentSerializer
    -from djangorestframework import mixins, views
    +from djangorestframework import mixins
    +from djangorestframework import views
     
    -class CommentRoot(mixins.ListModelQuerysetMixin,
    -                  mixins.CreateModelInstanceMixin,
    -                  views.BaseRootAPIView):
    +class CommentRoot(mixins.ListModelMixin,
    +                  mixins.CreateModelMixin,
    +                  views.MultipleObjectBaseView):
         model = Comment
         serializer_class = CommentSerializer
     
    -    get = list
    -    post = create
    +    def get(self, request, *args, **kwargs):
    +        return self.list(request, *args, **kwargs)
     
    -class CommentInstance(mixins.RetrieveModelInstanceMixin,
    -                      mixins.UpdateModelInstanceMixin,
    -                      mixins.DestroyModelInstanceMixin,
    -                      views.BaseInstanceAPIView):
    +    def post(self, request, *args, **kwargs):
    +        return self.create(request, *args, **kwargs)
    +
    +class CommentInstance(mixins.RetrieveModelMixin,
    +                      mixins.UpdateModelMixin,
    +                      mixins.DestroyModelMixin,
    +                      views.SingleObjectBaseView):
         model = Comment
         serializer_class = CommentSerializer
     
    -    get = retrieve
    -    put = update
    -    delete = destroy
    +    def get(self, request, *args, **kwargs):
    +        return self.retrieve(request, *args, **kwargs)
    +
    +    def put(self, request, *args, **kwargs):
    +        return self.update(request, *args, **kwargs)
    +
    +    def delete(self, request, *args, **kwargs):
    +        return self.destroy(request, *args, **kwargs)
     

    Reusing generic class based views

    That's a lot less code than before, but we can go one step further still. REST framework also provides a set of already mixed-in views.

    @@ -243,11 +245,11 @@ class CommentInstance(mixins.RetrieveModelInstanceMixin, from blog.serializers import CommentSerializer from djangorestframework import views -class CommentRoot(views.RootAPIView): +class CommentRoot(views.RootModelView): model = Comment serializer_class = CommentSerializer -class CommentInstance(views.InstanceAPIView): +class CommentInstance(views.InstanceModelView): model = Comment serializer_class = CommentSerializer
    diff --git a/tutorial/4-authentication-permissions-and-throttling.html b/tutorial/4-authentication-permissions-and-throttling.html index 1e666d71b..022237d11 100644 --- a/tutorial/4-authentication-permissions-and-throttling.html +++ b/tutorial/4-authentication-permissions-and-throttling.html @@ -94,7 +94,8 @@ margin-top: 5px;
  • Throttling
  • Exceptions
  • Status codes
  • -
  • Returning URLs
  • +
  • Returning URLs
  • +
  • Settings
  • diff --git a/tutorial/5-relationships-and-hyperlinked-apis.html b/tutorial/5-relationships-and-hyperlinked-apis.html index 2338a7575..997947639 100644 --- a/tutorial/5-relationships-and-hyperlinked-apis.html +++ b/tutorial/5-relationships-and-hyperlinked-apis.html @@ -94,7 +94,8 @@ margin-top: 5px;
  • Throttling
  • Exceptions
  • Status codes
  • -
  • Returning URLs
  • +
  • Returning URLs
  • +
  • Settings
  • diff --git a/tutorial/6-resource-orientated-projects.html b/tutorial/6-resource-orientated-projects.html index ddb5ce42f..4b3ccc989 100644 --- a/tutorial/6-resource-orientated-projects.html +++ b/tutorial/6-resource-orientated-projects.html @@ -94,7 +94,8 @@ margin-top: 5px;
  • Throttling
  • Exceptions
  • Status codes
  • -
  • Returning URLs
  • +
  • Returning URLs
  • +
  • Settings