From 9ae5a48332cb041ef4c56b775beb9ee98df07eb0 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 28 Aug 2014 17:33:56 +0100 Subject: [PATCH] Latest docs update --- api-guide/authentication.html | 9 +- api-guide/fields.html | 8 +- api-guide/generic-views.html | 14 +-- api-guide/routers.html | 99 +++++++++++++++++----- api-guide/serializers.html | 3 + api-guide/settings.html | 3 + api-guide/throttling.html | 7 +- api-guide/viewsets.html | 28 +++--- img/sponsors/2-wusawork.png | Bin 0 -> 12067 bytes index.html | 15 +--- topics/2.4-accouncement.html | 122 +++++++++++++++------------ topics/contributing.html | 28 +++++- topics/kickstarter-announcement.html | 8 +- topics/release-notes.html | 34 +++++++- tutorial/6-viewsets-and-routers.html | 8 +- 15 files changed, 262 insertions(+), 124 deletions(-) create mode 100644 img/sponsors/2-wusawork.png diff --git a/api-guide/authentication.html b/api-guide/authentication.html index bbce28355..9b11fbe5b 100644 --- a/api-guide/authentication.html +++ b/api-guide/authentication.html @@ -307,7 +307,9 @@ WSGIPassAuthorization On 'rest_framework.authtoken' ) -

Make sure to run manage.py syncdb after changing your settings. The authtoken database tables are managed by south (see Schema migrations below).

+
+

Note: Make sure to run manage.py syncdb after changing your settings. The rest_framework.authtoken app provides both Django (from v1.7) and South database migrations. See Schema migrations below.

+

You'll also need to create tokens for your users.

from rest_framework.authtoken.models import Token
 
@@ -362,7 +364,10 @@ for user in User.objects.all():
 

Note that the default obtain_auth_token view explicitly uses JSON requests and responses, rather than using default renderer and parser classes in your settings. If you need a customized version of the obtain_auth_token view, you can do so by overriding the ObtainAuthToken view class, and using that in your url conf instead.

Schema migrations

-

The rest_framework.authtoken app includes a south migration that will create the authtoken table.

+

The rest_framework.authtoken app includes both Django native migrations (for Django versions >1.7) and South migrations (for Django versions <1.7) that will create the authtoken table.

+
+

Note: From REST Framework v2.4.0 using South with Django <1.7 requires upgrading South v1.0+

+

If you're using a custom user model you'll need to make sure that any initial migration that creates the user table runs before the authtoken table is created.

You can do so by inserting a needed_by attribute in your user migration:

class Migration:
diff --git a/api-guide/fields.html b/api-guide/fields.html
index 72ef27e40..4ca9b1fb8 100644
--- a/api-guide/fields.html
+++ b/api-guide/fields.html
@@ -213,6 +213,7 @@ a.fusion-poweredby {
 
  • DRF Compound Fields
  • DRF Extra Fields
  • django-rest-framework-gis
  • +
  • django-rest-framework-hstore
  • @@ -329,10 +330,11 @@ class UserSerializer(serializers.ModelSerializer):

    A Boolean representation.

    Corresponds to django.db.models.fields.BooleanField.

    CharField

    -

    A text representation, optionally validates the text to be shorter than max_length and longer than min_length.

    +

    A text representation, optionally validates the text to be shorter than max_length and longer than min_length. +If allow_none is False (default), None values will be converted to an empty string.

    Corresponds to django.db.models.fields.CharField or django.db.models.fields.TextField.

    -

    Signature: CharField(max_length=None, min_length=None)

    +

    Signature: CharField(max_length=None, min_length=None, allow_none=False)

    URLField

    Corresponds to django.db.models.fields.URLField. Uses Django's django.core.validators.URLValidator for validation.

    Signature: URLField(max_length=200, min_length=None)

    @@ -461,6 +463,8 @@ class ColourField(serializers.WritableField):

    The drf-extra-fields package provides extra serializer fields for REST framework, including Base64ImageField and PointField classes.

    django-rest-framework-gis

    The django-rest-framework-gis package provides geographic addons for django rest framework like a GeometryField field and a GeoJSON serializer.

    +

    django-rest-framework-hstore

    +

    The django-rest-framework-hstore package provides an HStoreField to support django-hstore DictionaryField model field.

    diff --git a/api-guide/generic-views.html b/api-guide/generic-views.html index a9f96812c..be6e19f1a 100644 --- a/api-guide/generic-views.html +++ b/api-guide/generic-views.html @@ -262,7 +262,7 @@ class UserList(generics.ListCreateAPIView): serializer = UserSerializer(queryset, many=True) 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 the following entry.

    +

    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(model=User), name='user-list')
     

    @@ -300,7 +300,7 @@ class UserList(generics.ListCreateAPIView):

    get_queryset(self)

    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, or the default queryset for the model if the model shortcut is being used.

    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.

    +

    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):
         user = self.request.user
    @@ -308,7 +308,7 @@ class UserList(generics.ListCreateAPIView):
     

    get_object(self)

    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.

    +

    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):
         queryset = self.get_queryset()
    @@ -323,7 +323,7 @@ class UserList(generics.ListCreateAPIView):
     

    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.

    get_filter_backends(self)

    Returns the classes that should be used to filter the queryset. Defaults to returning the filter_backends attribute.

    -

    May be override to provide more complex behavior with filters, as using different (or even exlusive) lists of filter_backends depending on different criteria.

    +

    May be overridden to provide more complex behavior with filters, such as using different (or even exlusive) lists of filter_backends depending on different criteria.

    For example:

    def get_filter_backends(self):
         if "geo_route" in self.request.QUERY_PARAMS:
    @@ -335,7 +335,7 @@ class UserList(generics.ListCreateAPIView):
     

    get_serializer_class(self)

    Returns the class that should be used for the serializer. Defaults to returning the serializer_class attribute, or dynamically generating a serializer class if the model shortcut is being used.

    -

    May be override to provide dynamic behavior such as using different serializers for read and write operations, or providing different serializers to different types of users.

    +

    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):
         if self.request.user.is_staff:
    @@ -344,7 +344,7 @@ class UserList(generics.ListCreateAPIView):
     

    get_paginate_by(self)

    Returns the page size to use with pagination. By default this uses the paginate_by attribute, and may be overridden by the client if the paginate_by_param attribute is set.

    -

    You may want to override this method to provide more complex behavior such as modifying page sizes based on the media type of the response.

    +

    You may want to override this method to provide more complex behavior, such as modifying page sizes based on the media type of the response.

    For example:

    def get_paginate_by(self):
         if self.request.accepted_renderer.format == 'html':
    @@ -378,7 +378,7 @@ class UserList(generics.ListCreateAPIView):
     
     

    Mixins

    -

    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 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.

    ListModelMixin

    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.

    diff --git a/api-guide/routers.html b/api-guide/routers.html index 70dd61c49..77c6e5850 100644 --- a/api-guide/routers.html +++ b/api-guide/routers.html @@ -190,6 +190,7 @@ a.fusion-poweredby {
  • SimpleRouter
  • DefaultRouter
  • Custom Routers
  • +
  • Customizing dynamic routes
  • Example
  • Advanced custom routers
  • Third Party Packages
  • @@ -247,32 +248,36 @@ urlpatterns = router.urls

    This means you'll need to explicitly set the base_name argument when registering the viewset, as it could not be automatically determined from the model name.


    -

    Any methods on the viewset decorated with @link or @action will also be routed. +

    Any methods on the viewset decorated with @detail_route or @list_route will also be routed. For example, given a method like this on the UserViewSet class:

    from myapp.permissions import IsAdminOrIsSelf
    -from rest_framework.decorators import action
    +from rest_framework.decorators import detail_route
     
    -@action(permission_classes=[IsAdminOrIsSelf])
    -def set_password(self, request, pk=None):
    +class UserViewSet(ModelViewSet):
         ...
    +
    +    @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf])
    +    def set_password(self, request, pk=None):
    +        ...
     

    The following URL pattern would additionally be generated:

    • URL pattern: ^users/{pk}/set_password/$ Name: 'user-set-password'
    +

    For more information see the viewset documentation on marking extra actions for routing.

    API Guide

    SimpleRouter

    -

    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 @link or @action decorators.

    +

    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 @detail_route or @list_route decorators.

    + - - +
    URL StyleHTTP MethodActionURL Name
    {prefix}/GETlist{basename}-list
    POSTcreate
    {prefix}/{methodname}/GET, or as specified by `methods` argument`@list_route` decorated method{basename}-{methodname}
    {prefix}/{lookup}/GETretrieve{basename}-detail
    PUTupdate
    PATCHpartial_update
    DELETEdestroy
    {prefix}/{lookup}/{methodname}/GET@link decorated method{basename}-{methodname}
    POST@action decorated method
    {prefix}/{lookup}/{methodname}/GET, or as specified by `methods` argument`@detail_route` decorated method{basename}-{methodname}

    By default the URLs created by SimpleRouter are appended with a trailing slash. @@ -280,6 +285,11 @@ This behavior can be modified by setting the trailing_slash argumen

    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):
    +    lookup_field = 'my_model_id'
    +    lookup_value_regex = '[0-9a-f]{32}'
    +

    DefaultRouter

    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.

    @@ -287,12 +297,12 @@ This behavior can be modified by setting the trailing_slash argumen + - - +
    [.format]GETautomatically generated root viewapi-root
    {prefix}/[.format]GETlist{basename}-list
    POSTcreate
    {prefix}/{methodname}/[.format]GET, or as specified by `methods` argument`@list_route` decorated method{basename}-{methodname}
    {prefix}/{lookup}/[.format]GETretrieve{basename}-detail
    PUTupdate
    PATCHpartial_update
    DELETEdestroy
    {prefix}/{lookup}/{methodname}/[.format]GET@link decorated method{basename}-{methodname}
    POST@action decorated method
    {prefix}/{lookup}/{methodname}/[.format]GET, or as specified by `methods` argument`@detail_route` decorated method{basename}-{methodname}

    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.

    @@ -314,26 +324,75 @@ This behavior can be modified by setting the trailing_slash argumen
  • {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 suffix argument is reserved for identifying the viewset type, used when generating the view name and breadcrumb links.

    +

    Customizing dynamic routes

    +

    You can also customize how the @list_route and @detail_route decorators are routed. +To route either or both of these decorators, include a DynamicListRoute and/or DynamicDetailRoute named tuple in the .routes list.

    +

    The arguments to DynamicListRoute and DynamicDetailRoute are:

    +

    url: A string representing the URL to be routed. May include the same format strings as Route, and additionally accepts the {methodname} and {methodnamehyphen} format strings.

    +

    name: The name of the URL as used in reverse calls. May include the following format strings: {basename}, {methodname} and {methodnamehyphen}.

    +

    initkwargs: A dictionary of any additional arguments that should be passed when instantiating the view.

    Example

    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, SimpleRouter
    +
    from rest_framework.routers import Route, DynamicDetailRoute, SimpleRouter
     
    -class ReadOnlyRouter(SimpleRouter):
    +class CustomReadOnlyRouter(SimpleRouter):
         """
         A router for read-only APIs, which doesn't use trailing slashes.
         """
         routes = [
    -        Route(url=r'^{prefix}$',
    -              mapping={'get': 'list'},
    -              name='{basename}-list',
    -              initkwargs={'suffix': 'List'}),
    -        Route(url=r'^{prefix}/{lookup}$',
    -              mapping={'get': 'retrieve'},
    -              name='{basename}-detail',
    -              initkwargs={'suffix': 'Detail'})
    +        Route(
    +            url=r'^{prefix}$',
    +            mapping={'get': 'list'},
    +            name='{basename}-list',
    +            initkwargs={'suffix': 'List'}
    +        ),
    +        Route(
    +            url=r'^{prefix}/{lookup}$',
    +           mapping={'get': 'retrieve'},
    +           name='{basename}-detail',
    +           initkwargs={'suffix': 'Detail'}
    +        ),
    +        DynamicDetailRoute(
    +            url=r'^{prefix}/{lookup}/{methodnamehyphen}$',
    +            name='{basename}-{methodnamehyphen}',
    +            initkwargs={}
    +        )
         ]
     
    -

    The SimpleRouter class provides another example of setting the .routes attribute.

    +

    Let's take a look at the routes our CustomReadOnlyRouter would generate for a simple viewset.

    +

    views.py:

    +
    class UserViewSet(viewsets.ReadOnlyModelViewSet):
    +    """
    +    A viewset that provides the standard actions
    +    """
    +    queryset = User.objects.all()
    +    serializer_class = UserSerializer
    +    lookup_field = 'username'
    +
    +    @detail_route()
    +    def group_names(self, request):
    +        """
    +        Returns a list of all the group names that the given
    +        user belongs to.
    +        """
    +        user = self.get_object()
    +        groups = user.groups.all()
    +        return Response([group.name for group in groups])
    +
    +

    urls.py:

    +
    router = CustomReadOnlyRouter()
    +router.register('users', UserViewSet)
    +urlpatterns = router.urls
    +
    +

    The following mappings would be generated...

    + + + + + +
    URLHTTP MethodActionURL Name
    /usersGETlistuser-list
    /users/{username}GETretrieveuser-detail
    /users/{username}/group-namesGETgroup_namesuser-group-names
    + +

    For another example of setting the .routes attribute, see the source code for the SimpleRouter class.

    Advanced custom routers

    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_base_name(self, viewset) method, or else always explicitly set the base_name argument when registering your viewsets with the router.

    diff --git a/api-guide/serializers.html b/api-guide/serializers.html index 850b6dc59..f90aff167 100644 --- a/api-guide/serializers.html +++ b/api-guide/serializers.html @@ -209,6 +209,7 @@ a.fusion-poweredby {
  • Third party packages
  • MongoengineModelSerializer
  • GeoFeatureModelSerializer
  • +
  • HStoreSerializer
  • @@ -672,6 +673,8 @@ The ModelSerializer class lets you automatically create a Serialize

    The django-rest-framework-mongoengine package provides a MongoEngineModelSerializer serializer class that supports using MongoDB as the storage layer for Django REST framework.

    GeoFeatureModelSerializer

    The django-rest-framework-gis package provides a GeoFeatureModelSerializer serializer class that supports GeoJSON both for read and write operations.

    +

    HStoreSerializer

    +

    The django-rest-framework-hstore package provides an HStoreSerializer to support django-hstore DictionaryField model field and its schema-mode feature.

    diff --git a/api-guide/settings.html b/api-guide/settings.html index 880b3249d..598232632 100644 --- a/api-guide/settings.html +++ b/api-guide/settings.html @@ -434,6 +434,9 @@ If set to None then generic filtering is disabled.

    FORMAT_SUFFIX_KWARG

    The name of a parameter in the URL conf that may be used to provide a format suffix.

    Default: 'format'

    +

    NUM_PROXIES

    +

    An integer of 0 or more, that may be used to specify the number of application proxies that the API runs behind. This allows throttling to more accurately identify client IP addresses. If set to None then less strict IP matching will be used by the throttle classes.

    +

    Default: None

    diff --git a/api-guide/throttling.html b/api-guide/throttling.html index e0bb189c3..496424816 100644 --- a/api-guide/throttling.html +++ b/api-guide/throttling.html @@ -229,7 +229,7 @@ If any throttle check fails an exceptions.Throttled exception will 'DEFAULT_THROTTLE_RATES': { 'anon': '100/day', 'user': '1000/day' - } + } }

    The rate descriptions used in DEFAULT_THROTTLE_RATES may include second, minute, hour or day as the throttle period.

    @@ -257,6 +257,11 @@ def example_view(request, format=None): } return Response(content)
    +

    How clients are identified

    +

    The X-Forwarded-For and Remote-Addr HTTP headers are used to uniquely identify client IP addresses for throttling. If the X-Forwarded-For header is present then it will be used, otherwise the value of the Remote-Addr header will be used.

    +

    If you need to strictly identify unique client IP addresses, you'll need to first configure the number of application proxies that the API runs behind by setting the NUM_PROXIES setting. This setting should be an integer of zero or more. If set to non-zero then the client IP will be identified as being the last IP address in the X-Forwarded-For header, once any application proxy IP addresses have first been excluded. If set to zero, then the Remote-Addr header will always be used as the identifying IP address.

    +

    It is important to understand that if you configure the NUM_PROXIES setting, then all clients behind a unique NAT'd gateway will be treated as a single client.

    +

    Further context on how the X-Forwarded-For header works, and identifing a remote client IP can be found here.

    Setting up the cache

    The throttle classes provided by REST framework use Django's cache backend. You should make sure that you've set appropriate cache settings. The default value of LocMemCache backend should be okay for simple setups. See Django's cache documentation for more details.

    If you need to use a cache other than 'default', you can do so by creating a custom throttle class and setting the cache attribute. For example:

    diff --git a/api-guide/viewsets.html b/api-guide/viewsets.html index b154144ec..454d7d82f 100644 --- a/api-guide/viewsets.html +++ b/api-guide/viewsets.html @@ -186,7 +186,7 @@ a.fusion-poweredby {

    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.

    -

    Marking extra methods for routing

    +

    Marking extra actions for routing

    The default routers included with REST framework will provide routes for a standard set of create/retrieve/update/destroy style operations, as shown below:

    class UserViewSet(viewsets.ViewSet):
         """
    @@ -292,12 +292,13 @@ urlpatterns = router.urls
         def destroy(self, request, pk=None):
             pass
     
    -

    If you have ad-hoc methods that you need to be routed to, you can mark them as requiring routing using the @link or @action decorators. The @link decorator will route GET requests, and the @action decorator will route POST requests.

    +

    If you have ad-hoc methods that you need to be routed to, you can mark them as requiring routing using the @detail_route or @list_route decorators.

    +

    The @detail_route decorator contains pk in its URL pattern and is intended for methods which require a single instance. The @list_route decorator is intended for methods which operate on a list of objects.

    For example:

    from django.contrib.auth.models import User
    -from rest_framework import viewsets
     from rest_framework import status
    -from rest_framework.decorators import action
    +from rest_framework import viewsets
    +from rest_framework.decorators import detail_route, list_route
     from rest_framework.response import Response
     from myapp.serializers import UserSerializer, PasswordSerializer
     
    @@ -308,7 +309,7 @@ class UserViewSet(viewsets.ModelViewSet):
         queryset = User.objects.all()
         serializer_class = UserSerializer
     
    -    @action()
    +    @detail_route(methods=['post'])
         def set_password(self, request, pk=None):
             user = self.get_object()
             serializer = PasswordSerializer(data=request.DATA)
    @@ -319,14 +320,21 @@ class UserViewSet(viewsets.ModelViewSet):
             else:
                 return Response(serializer.errors,
                                 status=status.HTTP_400_BAD_REQUEST)
    +
    +    @list_route()
    +    def recent_users(self, request):
    +        recent_users = User.objects.all().order('-last_login')
    +        page = self.paginate_queryset(recent_users)
    +        serializer = self.get_pagination_serializer(page)
    +        return Response(serializer.data)
     
    -

    The @action and @link decorators can additionally take extra arguments that will be set for the routed view only. For example...

    -
        @action(permission_classes=[IsAdminOrIsSelf])
    +

    The decorators can additionally take extra arguments that will be set for the routed view only. For example...

    +
        @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf])
         def set_password(self, request, pk=None):
            ...
     
    -

    The @action decorator will route POST requests by default, but may also accept other HTTP methods, by using the methods argument. For example:

    -
        @action(methods=['POST', 'DELETE'])
    +

    Theses decorators will route GET requests by default, but may also accept other HTTP methods, by using the methods argument. For example:

    +
        @detail_route(methods=['post', 'delete'])
         def unset_password(self, request, pk=None):
            ...
     
    diff --git a/img/sponsors/2-wusawork.png b/img/sponsors/2-wusawork.png new file mode 100644 index 0000000000000000000000000000000000000000..5834729b95889107382e5f1c0bbd3c1fda48f842 GIT binary patch literal 12067 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 O>vA$m(p8ei!T$#{f=Utq literal 0 HcmV?d00001 diff --git a/index.html b/index.html index f58cdff1a..b7a5a2c2c 100644 --- a/index.html +++ b/index.html @@ -252,7 +252,7 @@ a.fusion-poweredby {

    REST framework requires the following:

    • Python (2.6.5+, 2.7, 3.2, 3.3)
    • -
    • Django (1.3, 1.4, 1.5, 1.6)
    • +
    • Django (1.4.2+, 1.5, 1.6, 1.7)

    The following packages are optional:

      @@ -389,16 +389,9 @@ urlpatterns = patterns('',
    • Credits

    Development

    -

    If you want to work on REST framework itself, clone the repository, then...

    -

    Build the docs:

    -
    ./mkdocs.py
    -
    -

    Run the tests:

    -
    ./rest_framework/runtests/runtests.py
    -
    -

    To run the tests against all supported configurations, first install the tox testing tool globally, using pip install tox, then simply run tox:

    -
    tox
    -
    +

    See the Contribution guidelines for information on how to clone +the repository, run the test suite and contribute changes back to REST +Framework.

    Support

    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.

    Paid support is available from DabApps, and can include work on REST framework core, or support with building your REST framework API. Please contact DabApps if you'd like to discuss commercial support options.

    diff --git a/topics/2.4-accouncement.html b/topics/2.4-accouncement.html index 92e21a5d0..0b4df1a88 100644 --- a/topics/2.4-accouncement.html +++ b/topics/2.4-accouncement.html @@ -3,17 +3,17 @@ REST framework 2.4 announcement - Django REST framework - - + + - - - - + + + + - - - + + +