diff --git a/.gitignore b/.gitignore index 2255cd9aa..ae73f8379 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ .* html/ +htmlcov/ coverage/ build/ dist/ diff --git a/README.md b/README.md index 12ed09f9f..62883e32e 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,11 @@ Django REST framework is a powerful and flexible toolkit that makes it easy to b Some reasons you might want to use REST framework: -* The Web browseable API is a huge useability win for your developers. -* Authentication policies including OAuth1a and OAuth2 out of the box. -* Serialization that supports both ORM and non-ORM data sources. -* Customizable all the way down - just use regular function-based views if you don't need the more powerful features. -* Extensive documentation, and great community support. +* The [Web browseable API][sandbox] is a huge useability win for your developers. +* [Authentication policies][authentication] including [OAuth1a][oauth1-section] and [OAuth2][oauth2-section] out of the box. +* [Serialization][serializers] that supports both [ORM][modelserializer-section] and [non-ORM][serializer-section] data sources. +* Customizable all the way down - just use [regular function-based views][functionview-section] if you don't need the [more][generic-views] [powerful][viewsets] [features][routers]. +* [Extensive documentation][index], and [great community support][group]. There is a live example API for testing purposes, [available here][sandbox]. @@ -139,6 +139,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework [0.4]: https://github.com/tomchristie/django-rest-framework/tree/0.4.X [sandbox]: http://restframework.herokuapp.com/ + +[index]: http://django-rest-framework.org/ +[oauth1-section]: http://django-rest-framework.org/api-guide/authentication.html#oauthauthentication +[oauth2-section]: http://django-rest-framework.org/api-guide/authentication.html#oauth2authentication +[serializer-section]: http://django-rest-framework.org/api-guide/serializers.html#serializers +[modelserializer-section]: http://django-rest-framework.org/api-guide/serializers.html#modelserializer +[functionview-section]: http://django-rest-framework.org/api-guide/views.html#function-based-views +[generic-views]: http://django-rest-framework.org/api-guide/generic-views.html +[viewsets]: http://django-rest-framework.org/api-guide/viewsets.html +[routers]: http://django-rest-framework.org/api-guide/routers.html +[serializers]: http://django-rest-framework.org/api-guide/serializers.html +[authentication]: http://django-rest-framework.org/api-guide/authentication.html + [rest-framework-2-announcement]: http://django-rest-framework.org/topics/rest-framework-2-announcement.html [2.1.0-notes]: https://groups.google.com/d/topic/django-rest-framework/Vv2M0CMY9bg/discussion [image]: http://django-rest-framework.org/img/quickstart.png diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 8cf995b38..7caeac1e2 100755 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -46,6 +46,11 @@ The default authentication schemes may be set globally, using the `DEFAULT_AUTHE You can also set the authentication scheme on a per-view or per-viewset basis, using the `APIView` class based views. + from rest_framework.authentication import SessionAuthentication, BasicAuthentication + from rest_framework.permissions import IsAuthenticated + from rest_framework.response import Response + from rest_framework.views import APIView + class ExampleView(APIView): authentication_classes = (SessionAuthentication, BasicAuthentication) permission_classes = (IsAuthenticated,) @@ -121,7 +126,7 @@ To use the `TokenAuthentication` scheme, include `rest_framework.authtoken` in y 'rest_framework.authtoken' ) -Make sure to run `manage.py syncdb` after changing your settings. +Make sure to run `manage.py syncdb` after changing your settings. The `authtoken` database tables are managed by south (see [Schema migrations](#schema-migrations) below). You'll also need to create tokens for your users. @@ -157,11 +162,16 @@ The `curl` command line tool may be useful for testing token authenticated APIs. If you want every user to have an automatically generated Token, you can simply catch the User's `post_save` signal. + from django.dispatch import receiver + from rest_framework.authtoken.models import Token + @receiver(post_save, sender=User) def create_auth_token(sender, instance=None, created=False, **kwargs): if created: Token.objects.create(user=instance) +Note that you'll want to ensure you place this code snippet in an installed `models.py` module, or some other location that will be imported by Django on startup. + If you've already created some users, you can generate tokens for all existing users like this: from django.contrib.auth.models import User @@ -184,9 +194,11 @@ The `obtain_auth_token` view will return a JSON response when valid `username` a Note that the default `obtain_auth_token` view explicitly uses JSON requests and responses, rather than using default renderer and parser classes in your settings. If you need a customized version of the `obtain_auth_token` view, you can do so by overriding the `ObtainAuthToken` view class, and using that in your url conf instead. -#### Custom user models +#### Schema migrations -The `rest_framework.authtoken` app includes a south migration that will create the authtoken table. If you're using a [custom user model][custom-user-model] you'll need to make sure that any initial migration that creates the user table runs before the authtoken table is created. +The `rest_framework.authtoken` app includes a south migration that will create the authtoken table. + +If you're using a [custom user model][custom-user-model] you'll need to make sure that any initial migration that creates the user table runs before the authtoken table is created. You can do so by inserting a `needed_by` attribute in your user migration: @@ -201,6 +213,12 @@ You can do so by inserting a `needed_by` attribute in your user migration: For more details, see the [south documentation on dependencies][south-dependencies]. +Also note that if you're using a `post_save` signal to create tokens, then the first time you create the database tables, you'll need to ensure any migrations are run prior to creating any superusers. For example: + + python manage.py syncdb --noinput # Won't create a superuser just yet, due to `--noinput`. + python manage.py migrate + python manage.py createsuperuser + ## SessionAuthentication This authentication scheme uses Django's default session backend for authentication. Session authentication is appropriate for AJAX clients that are running in the same session context as your website. @@ -328,6 +346,10 @@ If the `.authenticate_header()` method is not overridden, the authentication sch The following example will authenticate any incoming request as the user given by the username in a custom request header named 'X_USERNAME'. + from django.contrib.auth.models import User + from rest_framework import authentication + from rest_framework import exceptions + class ExampleAuthentication(authentication.BaseAuthentication): def authenticate(self, request): username = request.META.get('X_USERNAME') @@ -355,6 +377,10 @@ HTTP digest authentication is a widely implemented scheme that was intended to r The [Django OAuth Toolkit][django-oauth-toolkit] package provides OAuth 2.0 support, and works with Python 2.7 and Python 3.3+. The package is maintained by [Evonove][evonove] and uses the excelllent [OAuthLib][oauthlib]. The package is well documented, and comes as a recommended alternative for OAuth 2.0 support. +## Django OAuth2 Consumer + +The [Django OAuth2 Consumer][doac] library from [Rediker Software][rediker] is another package that provides [OAuth 2.0 support for REST framework][doac-rest-framework]. The package includes token scoping permissions on tokens, which allows finer-grained access to your API. + [cite]: http://jacobian.org/writing/rest-worst-practices/ [http401]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2 [http403]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4 @@ -376,3 +402,6 @@ The [Django OAuth Toolkit][django-oauth-toolkit] package provides OAuth 2.0 supp [django-oauth-toolkit]: https://github.com/evonove/django-oauth-toolkit [evonove]: https://github.com/evonove/ [oauthlib]: https://github.com/idan/oauthlib +[doac]: https://github.com/Rediker-Software/doac +[rediker]: https://github.com/Rediker-Software +[doac-rest-framework]: https://github.com/Rediker-Software/doac/blob/master/docs/integrations.md# diff --git a/docs/api-guide/content-negotiation.md b/docs/api-guide/content-negotiation.md index 10288c94d..94dd59cac 100644 --- a/docs/api-guide/content-negotiation.md +++ b/docs/api-guide/content-negotiation.md @@ -43,13 +43,19 @@ This is a valid approach as the HTTP spec deliberately underspecifies how a serv It's unlikely that you'll want to provide a custom content negotiation scheme for REST framework, but you can do so if needed. To implement a custom content negotiation scheme override `BaseContentNegotiation`. -REST framework's content negotiation classes handle selection of both the appropriate parser for the request, and the appropriate renderer for the response, so you should implement both the `.select_parser(request, parsers)` and `.select_renderer(request, renderers, format_suffix)` methods. +REST framework's content negotiation classes handle selection of both the appropriate parser for the request, and the appropriate renderer for the response, so you should implement both the `.select_parser(request, parsers)` and `.select_renderer(request, renderers, format_suffix)` methods. + +The `select_parser()` method should return one of the parser instances from the list of available parsers, or `None` if none of the parsers can handle the incoming request. + +The `select_renderer()` method should return a two-tuple of (renderer instance, media type), or raise a `NotAcceptable` exception. ## Example The following is a custom content negotiation class which ignores the client request when selecting the appropriate parser or renderer. + from rest_framework.negotiation import BaseContentNegotiation + class IgnoreClientContentNegotiation(BaseContentNegotiation): def select_parser(self, request, parsers): """ @@ -61,6 +67,31 @@ request when selecting the appropriate parser or renderer. """ Select the first renderer in the `.renderer_classes` list. """ - return renderers[0] + return (renderers[0], renderers[0].media_type) + +## Setting the content negotiation + +The default content negotiation class may be set globally, using the `DEFAULT_CONTENT_NEGOTIATION_CLASS` setting. For example, the following settings would use our example `IgnoreClientContentNegotiation` class. + + REST_FRAMEWORK = { + 'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'myapp.negotiation.IgnoreClientContentNegotiation', + } + +You can also set the content negotiation used for an individual view, or viewset, using the `APIView` class based views. + + from myapp.negotiation import IgnoreClientContentNegotiation + from rest_framework.response import Response + from rest_framework.views import APIView + + class NoNegotiationView(APIView): + """ + An example view that does not perform content negotiation. + """ + content_negotiation_class = IgnoreClientContentNegotiation + + def get(self, request, format=None): + return Response({ + 'accepted media type': request.accepted_renderer.media_type + }) [accept-header]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index d69730c98..962c49e2a 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -78,6 +78,9 @@ A generic, **read-only** field. You can use this field for any attribute that d For example, using the following model. + from django.db import models + from django.utils.timezone import now + class Account(models.Model): owner = models.ForeignKey('auth.user') name = models.CharField(max_length=100) @@ -85,13 +88,14 @@ For example, using the following model. payment_expiry = models.DateTimeField() def has_expired(self): - now = datetime.datetime.now() - return now > self.payment_expiry + return now() > self.payment_expiry A serializer definition that looked like this: + from rest_framework import serializers + class AccountSerializer(serializers.HyperlinkedModelSerializer): - expired = Field(source='has_expired') + expired = serializers.Field(source='has_expired') class Meta: fields = ('url', 'owner', 'name', 'expired') @@ -125,12 +129,11 @@ The `ModelField` class is generally intended for internal use, but can be used b This is a read-only field. It gets its value by calling a method on the serializer class it is attached to. It can be used to add any sort of data to the serialized representation of your object. The field's constructor accepts a single argument, which is the name of the method on the serializer to be called. The method should accept a single argument (in addition to `self`), which is the object being serialized. It should return whatever you want to be included in the serialized representation of the object. For example: - from rest_framework import serializers from django.contrib.auth.models import User from django.utils.timezone import now + from rest_framework import serializers class UserSerializer(serializers.ModelSerializer): - days_since_joined = serializers.SerializerMethodField('get_days_since_joined') class Meta: diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index 05c997a39..649462da7 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -20,6 +20,10 @@ You can do so by filtering based on the value of `request.user`. For example: + from myapp.models import Purchase + from myapp.serializers import PurchaseSerializer + from rest_framework import generics + class PurchaseList(generics.ListAPIView) serializer_class = PurchaseSerializer @@ -90,6 +94,11 @@ The default filter backends may be set globally, using the `DEFAULT_FILTER_BACKE You can also set the filter backends on a per-view, or per-viewset basis, using the `GenericAPIView` class based views. + from django.contrib.auth.models import User + from myapp.serializers import UserSerializer + from rest_framework import filters + from rest_framework import generics + class UserListView(generics.ListAPIView): queryset = User.objects.all() serializer = UserSerializer @@ -150,6 +159,11 @@ This will automatically create a `FilterSet` class for the given fields, and wil For more advanced filtering requirements you can specify a `FilterSet` class that should be used by the view. For example: + import django_filters + from myapp.models import Product + from myapp.serializers import ProductSerializer + from rest_framework import generics + class ProductFilter(django_filters.FilterSet): min_price = django_filters.NumberFilter(lookup_type='gte') max_price = django_filters.NumberFilter(lookup_type='lte') diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index cd1bc7a1c..7185b6b68 100755 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -17,6 +17,11 @@ If the generic views don't suit the needs of your API, you can drop down to usin Typically when using the generic views, you'll override the view, and set several class attributes. + from django.contrib.auth.models import User + from myapp.serializers import UserSerializer + from rest_framework import generics + from rest_framework.permissions import IsAdminUser + class UserList(generics.ListCreateAPIView): queryset = User.objects.all() serializer_class = UserSerializer @@ -40,7 +45,7 @@ For more complex cases you might also want to override various methods on the vi 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. - url(r'^/users/', ListCreateAPIView.as_view(model=User) name='user-list') + url(r'^/users/', ListCreateAPIView.as_view(model=User), name='user-list') --- @@ -68,7 +73,7 @@ The following attributes control the basic view behavior. **Pagination**: -The following attibutes are used to control pagination when used with list views. +The following attributes are used to control pagination when used with list views. * `paginate_by` - The size of pages to use with paginated data. If set to `None` then pagination is turned off. If unset this uses the same value as the `PAGINATE_BY` setting, which defaults to `None`. * `paginate_by_param` - The name of a query parameter, which can be used by the client to override the default page size to use for pagination. If unset this uses the same value as the `PAGINATE_BY_PARAM` setting, which defaults to `None`. @@ -92,7 +97,8 @@ May be overridden to provide dynamic behavior such as returning a queryset that For example: def get_queryset(self): - return self.user.accounts.all() + user = self.request.user + return user.accounts.all() #### `get_object(self)` @@ -107,7 +113,12 @@ For example: filter = {} for field in self.multiple_lookup_fields: filter[field] = self.kwargs[field] - return get_object_or_404(queryset, **filter) + + obj = get_object_or_404(queryset, **filter) + self.check_object_permissions(self.request, obj) + 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. #### `get_serializer_class(self)` @@ -124,7 +135,7 @@ For example: #### `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 cient if the `paginate_by_param` attribute is set. +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. diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index 912ce41bd..0829589f8 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -13,6 +13,7 @@ REST framework includes a `PaginationSerializer` class that makes it easy to ret Let's start by taking a look at an example from the Django documentation. from django.core.paginator import Paginator + objects = ['john', 'paul', 'george', 'ringo'] paginator = Paginator(objects, 2) page = paginator.page(1) @@ -22,6 +23,7 @@ Let's start by taking a look at an example from the Django documentation. At this point we've got a page object. If we wanted to return this page object as a JSON response, we'd need to provide the client with context such as next and previous links, so that it would be able to page through the remaining results. from rest_framework.pagination import PaginationSerializer + serializer = PaginationSerializer(instance=page) serializer.data # {'count': 4, 'next': '?page=2', 'previous': None, 'results': [u'john', u'paul']} @@ -83,11 +85,12 @@ We could now use our pagination serializer in a view like this. The generic class based views `ListAPIView` and `ListCreateAPIView` provide pagination of the returned querysets by default. You can customise this behaviour by altering the pagination style, by modifying the default number of results, by allowing clients to override the page size using a query parameter, or by turning pagination off completely. -The default pagination style may be set globally, using the `DEFAULT_PAGINATION_SERIALIZER_CLASS`, `PAGINATE_BY` and `PAGINATE_BY_PARAM` settings. For example. +The default pagination style may be set globally, using the `DEFAULT_PAGINATION_SERIALIZER_CLASS`, `PAGINATE_BY`, `PAGINATE_BY_PARAM`, and `MAX_PAGINATE_BY` settings. For example. REST_FRAMEWORK = { - 'PAGINATE_BY': 10, - 'PAGINATE_BY_PARAM': 'page_size' + 'PAGINATE_BY': 10, # Default to 10 + 'PAGINATE_BY_PARAM': 'page_size', # Allow client to override, using `?page_size=xxx`. + 'MAX_PAGINATE_BY': 100 # Maximum limit allowed when using `?page_size=xxx`. } You can also set the pagination style on a per-view basis, using the `ListAPIView` generic class-based view. @@ -97,6 +100,7 @@ You can also set the pagination style on a per-view basis, using the `ListAPIVie serializer_class = ExampleModelSerializer paginate_by = 10 paginate_by_param = 'page_size' + max_paginate_by = 100 Note that using a `paginate_by` value of `None` will turn off pagination for the view. @@ -114,6 +118,9 @@ You can also override the name used for the object list field, by setting the `r For example, to nest a pair of links labelled 'prev' and 'next', and set the name for the results field to 'objects', you might use something like this. + from rest_framework import pagination + from rest_framework import serializers + class LinksSerializer(serializers.Serializer): next = pagination.NextPageField(source='*') prev = pagination.PreviousPageField(source='*') @@ -135,7 +142,7 @@ To have your custom pagination serializer be used by default, use the `DEFAULT_P Alternatively, to set your custom pagination serializer on a per-view basis, use the `pagination_serializer_class` attribute on a generic class based view: - class PaginatedListView(ListAPIView): + class PaginatedListView(generics.ListAPIView): model = ExampleModel pagination_serializer_class = CustomPaginationSerializer paginate_by = 10 diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md index 5bd79a317..1030fcb65 100644 --- a/docs/api-guide/parsers.md +++ b/docs/api-guide/parsers.md @@ -34,9 +34,13 @@ The default set of parsers may be set globally, using the `DEFAULT_PARSER_CLASSE ) } -You can also set the renderers used for an individual view, or viewset, +You can also set the parsers used for an individual view, or viewset, using the `APIView` class based views. + from rest_framework.parsers import YAMLParser + from rest_framework.response import Response + from rest_framework.views import APIView + class ExampleView(APIView): """ A view that can accept POST requests with YAML content. diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 2c0a055c8..a7bf15556 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -25,9 +25,17 @@ Object level permissions are run by REST framework's generic views when `.get_ob As with view level permissions, an `exceptions.PermissionDenied` exception will be raised if the user is not allowed to act on the given object. If you're writing your own views and want to enforce object level permissions, -you'll need to explicitly call the `.check_object_permissions(request, obj)` method on the view at the point at which you've retrieved the object. +or if you override the `get_object` method on a generic view, then you'll need to explicitly call the `.check_object_permissions(request, obj)` method on the view at the point at which you've retrieved the object. + This will either raise a `PermissionDenied` or `NotAuthenticated` exception, or simply return if the view has the appropriate permissions. +For example: + + def get_object(self): + obj = get_object_or_404(self.get_queryset()) + self.check_object_permissions(self.request, obj) + return obj + ## Setting the permission policy The default permission policy may be set globally, using the `DEFAULT_PERMISSION_CLASSES` setting. For example. @@ -47,6 +55,10 @@ If not specified, this setting defaults to allowing unrestricted access: You can also set the authentication policy on a per-view, or per-viewset basis, using the `APIView` class based views. + from rest_framework.permissions import IsAuthenticated + from rest_framework.responses import Response + from rest_framework.views import APIView + class ExampleView(APIView): permission_classes = (IsAuthenticated,) @@ -147,7 +159,7 @@ If you need to test if a request is a read operation or a write operation, you s **Note**: In versions 2.0 and 2.1, the signature for the permission checks always included an optional `obj` parameter, like so: `.has_permission(self, request, view, obj=None)`. The method would be called twice, first for the global permission checks, with no object supplied, and second for the object-level check when required. -As of version 2.2 this signature has now been replaced with two separate method calls, which is more explict and obvious. The old style signature continues to work, but it's use will result in a `PendingDeprecationWarning`, which is silent by default. In 2.3 this will be escalated to a `DeprecationWarning`, and in 2.4 the old-style signature will be removed. +As of version 2.2 this signature has now been replaced with two separate method calls, which is more explicit and obvious. The old style signature continues to work, but its use will result in a `PendingDeprecationWarning`, which is silent by default. In 2.3 this will be escalated to a `DeprecationWarning`, and in 2.4 the old-style signature will be removed. For more details see the [2.2 release announcement][2.2-announcement]. @@ -157,6 +169,8 @@ For more details see the [2.2 release announcement][2.2-announcement]. The following is an example of a permission class that checks the incoming request's IP address against a blacklist, and denies the request if the IP has been blacklisted. + from rest_framework import permissions + class BlacklistPermission(permissions.BasePermission): """ Global permission check for blacklisted IPs. @@ -188,6 +202,20 @@ Note that the generic views will check the appropriate object level permissions, Also note that the generic views will only check the object-level permissions for views that retrieve a single model instance. If you require object-level filtering of list views, you'll need to filter the queryset separately. See the [filtering documentation][filtering] for more details. +--- + +# Third party packages + +The following third party packages are also available. + +## DRF Any Permissions + +The [DRF Any Permissions][drf-any-permissions] packages provides a different permission behavior in contrast to REST framework. Instead of all specified permissions being required, only one of the given permissions has to be true in order to get access to the view. + +## Composed Permissions + +The [Composed Permissions][composed-permissions] package provides a simple way to define complex and multi-depth (with logic operators) permission objects, using small and reusable components. + [cite]: https://developer.apple.com/library/mac/#documentation/security/Conceptual/AuthenticationAndAuthorizationGuide/Authorization/Authorization.html [authentication]: authentication.md [throttling]: throttling.md @@ -197,3 +225,5 @@ Also note that the generic views will only check the object-level permissions fo [django-oauth2-provider]: https://github.com/caffeinehit/django-oauth2-provider [2.2-announcement]: ../topics/2.2-announcement.md [filtering]: filtering.md +[drf-any-permissions]: https://github.com/kevin-brown/drf-any-permissions +[composed-permissions]: https://github.com/niwibe/djangorestframework-composed-permissions diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md index 50c9bc546..5ec4b22fc 100644 --- a/docs/api-guide/relations.md +++ b/docs/api-guide/relations.md @@ -39,7 +39,7 @@ In order to explain the various types of relational fields, we'll use a couple o ## RelatedField -`RelatedField` may be used to represent the target of the relationship using it's `__unicode__` method. +`RelatedField` may be used to represent the target of the relationship using its `__unicode__` method. For example, the following serializer. @@ -71,12 +71,12 @@ This field is read only. ## PrimaryKeyRelatedField -`PrimaryKeyRelatedField` may be used to represent the target of the relationship using it's primary key. +`PrimaryKeyRelatedField` may be used to represent the target of the relationship using its primary key. For example, the following serializer: class AlbumSerializer(serializers.ModelSerializer): - tracks = PrimaryKeyRelatedField(many=True, read_only=True) + tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True) class Meta: model = Album @@ -110,8 +110,8 @@ By default this field is read-write, although you can change this behavior using For example, the following serializer: class AlbumSerializer(serializers.ModelSerializer): - tracks = HyperlinkedRelatedField(many=True, read_only=True, - view_name='track-detail') + tracks = serializers.HyperlinkedRelatedField(many=True, read_only=True, + view_name='track-detail') class Meta: model = Album @@ -148,7 +148,8 @@ By default this field is read-write, although you can change this behavior using For example, the following serializer: class AlbumSerializer(serializers.ModelSerializer): - tracks = SlugRelatedField(many=True, read_only=True, slug_field='title') + tracks = serializers.SlugRelatedField(many=True, read_only=True, + slug_field='title') class Meta: model = Album @@ -183,7 +184,7 @@ When using `SlugRelatedField` as a read-write field, you will normally want to e This field can be applied as an identity relationship, such as the `'url'` field on a HyperlinkedModelSerializer. It can also be used for an attribute on the object. For example, the following serializer: class AlbumSerializer(serializers.HyperlinkedModelSerializer): - track_listing = HyperlinkedIdentityField(view_name='track-list') + track_listing = serializers.HyperlinkedIdentityField(view_name='track-list') class Meta: model = Album @@ -213,8 +214,6 @@ Nested relationships can be expressed by using serializers as fields. If the field is used to represent a to-many relationship, you should add the `many=True` flag to the serializer field. -Note that nested relationships are currently read-only. For read-write relationships, you should use a flat relational style. - ## Example For example, the following serializer: @@ -252,7 +251,7 @@ If you want to implement a read-write relational field, you must also implement ## Example -For, example, we could define a relational field, to serialize a track to a custom string representation, using it's ordering, title, and duration. +For, example, we could define a relational field, to serialize a track to a custom string representation, using its ordering, title, and duration. import time @@ -386,7 +385,7 @@ For more information see [the Django documentation on generic relations][generic By default, relational fields that target a ``ManyToManyField`` with a ``through`` model specified are set to read-only. -If you exlicitly specify a relational field pointing to a +If you explicitly specify a relational field pointing to a ``ManyToManyField`` with a through model, be sure to set ``read_only`` to ``True``. @@ -422,7 +421,7 @@ For example, if all your object URLs used both a account and a slug in the the U def get_object(self, queryset, view_name, view_args, view_kwargs): account = view_kwargs['account'] slug = view_kwargs['slug'] - return queryset.get(account=account, slug=sug) + return queryset.get(account=account, slug=slug) --- diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index b627c9306..657377d92 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -30,11 +30,16 @@ The default set of renderers may be set globally, using the `DEFAULT_RENDERER_CL You can also set the renderers used for an individual view, or viewset, using the `APIView` class based views. + from django.contrib.auth.models import User + from rest_framework.renderers import JSONRenderer, YAMLRenderer + from rest_framework.response import Response + from rest_framework.views import APIView + class UserCountView(APIView): """ - A view that returns the count of active users, in JSON or JSONp. + A view that returns the count of active users, in JSON or YAML. """ - renderer_classes = (JSONRenderer, JSONPRenderer) + renderer_classes = (JSONRenderer, YAMLRenderer) def get(self, request, format=None): user_count = User.objects.filter(active=True).count() @@ -83,7 +88,7 @@ The client may additionally include an `'indent'` media type parameter, in which **.format**: `'.json'` -**.charset**: `utf-8` +**.charset**: `None` ## UnicodeJSONRenderer @@ -105,7 +110,7 @@ Both the `JSONRenderer` and `UnicodeJSONRenderer` styles conform to [RFC 4627][r **.format**: `'.json'` -**.charset**: `utf-8` +**.charset**: `None` ## JSONPRenderer @@ -207,6 +212,20 @@ You can use `TemplateHTMLRenderer` either to return regular HTML pages using RES See also: `TemplateHTMLRenderer` +## HTMLFormRenderer + +Renders data returned by a serializer into an HTML form. The output of this renderer does not include the enclosing `
` tags or an submit actions, as you'll probably need those to include the desired method and URL. Also note that the `HTMLFormRenderer` does not yet support including field error messages. + +Note that the template used by the `HTMLFormRenderer` class, and the context submitted to it **may be subject to change**. If you need to use this renderer class it is advised that you either make a local copy of the class and templates, or follow the release note on REST framework upgrades closely. + +**.media_type**: `text/html` + +**.format**: `'.form'` + +**.charset**: `utf-8` + +**.template**: `'rest_framework/form.html'` + ## BrowsableAPIRenderer 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. @@ -217,13 +236,33 @@ Renders data into HTML for the Browsable API. This renderer will determine whic **.charset**: `utf-8` +**.template**: `'rest_framework/api.html'` + +#### Customizing BrowsableAPIRenderer + +By default the response content will be rendered with the highest priority renderer apart from `BrowseableAPIRenderer`. 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): + def get_default_renderer(self, view): + return JSONRenderer() + +## MultiPartRenderer + +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][testing]. + +**.media_type**: `multipart/form-data; boundary=BoUnDaRyStRiNg` + +**.format**: `'.multipart'` + +**.charset**: `utf-8` + --- # Custom renderers 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 wil be used as the body of the HTTP response. +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: @@ -272,12 +311,15 @@ By default renderer classes are assumed to be using the `UTF-8` encoding. To us 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. Doing so will also ensure that the browsable API will not attempt to display the binary content as a string. +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): media_type = 'image/jpeg' format = 'jpg' charset = None + render_style = 'binary' def render(self, data, media_type=None, renderer_context=None): return data @@ -373,6 +415,7 @@ Comma-separated values are a plain-text tabular data format, that can be easily [rfc4627]: http://www.ietf.org/rfc/rfc4627.txt [cors]: http://www.w3.org/TR/cors/ [cors-docs]: ../topics/ajax-csrf-cors.md +[testing]: testing.md [HATEOAS]: http://timelessrepo.com/haters-gonna-hateoas [quote]: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven [application/vnd.github+json]: http://developer.github.com/v3/media/ diff --git a/docs/api-guide/requests.md b/docs/api-guide/requests.md index 39a34fcfb..0696fedf6 100644 --- a/docs/api-guide/requests.md +++ b/docs/api-guide/requests.md @@ -117,7 +117,7 @@ For more information see the [browser enhancements documentation]. # Standard HttpRequest attributes -As REST framework's `Request` extends Django's `HttpRequest`, all the other standard attributes and methods are also available. For example the `request.META` dictionary is available as normal. +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. diff --git a/docs/api-guide/responses.md b/docs/api-guide/responses.md index f83b8194a..5a42aa923 100644 --- a/docs/api-guide/responses.md +++ b/docs/api-guide/responses.md @@ -10,7 +10,7 @@ REST framework supports HTTP content negotiation by providing a `Response` class 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` objects from your views if you want, but it provides a nicer interface for returning Web API responses. +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. @@ -24,7 +24,7 @@ Unless you want to heavily customize REST framework for some reason, you should 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 primative datatypes before creating the `Response` object. +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. @@ -54,7 +54,7 @@ The rendered content of the response. The `.render()` method must have been cal ## .template_name -The `template_name`, if supplied. Only required if `HTMLRenderer` or some other custom template renderer is the accepted renderer for the reponse. +The `template_name`, if supplied. Only required if `HTMLRenderer` or some other custom template renderer is the accepted renderer for the response. ## .accepted_renderer diff --git a/docs/api-guide/reverse.md b/docs/api-guide/reverse.md index 19930dc3f..383eca4ce 100644 --- a/docs/api-guide/reverse.md +++ b/docs/api-guide/reverse.md @@ -17,7 +17,7 @@ The advantages of doing so are: REST framework provides two utility functions to make it more simple to return absolute URIs from your Web API. -There's no requirement for you to use them, but if you do then the self-describing API will be able to automatically hyperlink it's output for you, which makes browsing the API much easier. +There's no requirement for you to use them, but if you do then the self-describing API will be able to automatically hyperlink its output for you, which makes browsing the API much easier. ## reverse @@ -27,13 +27,13 @@ Has the same behavior as [`django.core.urlresolvers.reverse`][reverse], except t You should **include the request as a keyword argument** to the function, for example: - import datetime from rest_framework.reverse import reverse from rest_framework.views import APIView + from django.utils.timezone import now class APIRootView(APIView): def get(self, request): - year = datetime.datetime.now().year + year = now().year data = { ... 'year-summary-url': reverse('year-summary', args=[year], request=request) diff --git a/docs/api-guide/routers.md b/docs/api-guide/routers.md index f16fa9468..fb48197e9 100644 --- a/docs/api-guide/routers.md +++ b/docs/api-guide/routers.md @@ -14,6 +14,8 @@ REST framework adds support for automatic URL routing to Django, and provides yo Here's an example of a simple URL conf, that uses `DefaultRouter`. + from rest_framework import routers + router = routers.SimpleRouter() router.register(r'users', UserViewSet) router.register(r'accounts', AccountViewSet) @@ -26,7 +28,7 @@ There are two mandatory arguments to the `register()` method: Optionally, you may also specify an additional argument: -* `base_name` - The base to use for the URL names that are created. If unset the basename will be automatically generated based on the `model` or `queryset` attribute on the viewset, if it has one. +* `base_name` - The base to use for the URL names that are created. If unset the basename will be automatically generated based on the `model` or `queryset` attribute on the viewset, if it has one. Note that if the viewset does not include a `model` or `queryset` attribute then you must set `base_name` when registering the viewset. The example above would generate the following URL patterns: @@ -38,7 +40,10 @@ The example above would generate the following URL patterns: ### Extra link and actions Any methods on the viewset decorated with `@link` or `@action` will also be routed. -For example, a given method like this on the `UserViewSet` class: +For example, given a method like this on the `UserViewSet` class: + + from myapp.permissions import IsAdminOrIsSelf + from rest_framework.decorators import action @action(permission_classes=[IsAdminOrIsSelf]) def set_password(self, request, pk=None): @@ -66,7 +71,7 @@ This router includes routes for the standard set of `list`, `create`, `retrieve` POST@action decorated method -By default the URLs created by `SimpleRouter` are appending with a trailing slash. +By default the URLs created by `SimpleRouter` are appended with a trailing slash. This behavior can be modified by setting the `trailing_slash` argument to `False` when instantiating the router. For example: router = SimpleRouter(trailing_slash=False) @@ -90,32 +95,58 @@ This router is similar to `SimpleRouter` as above, but additionally includes a d POST@action decorated method -As with `SimpleRouter` the trailing slashs on the URL routes can be removed by setting the `trailing_slash` argument to `False` when instantiating the router. +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) # Custom Routers -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 your URLs for your API are strutured. 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. +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 your 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 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 `suffix` argument is reserved for identifying the viewset type, used when generating the view name and breadcrumb links. ## 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 + class ReadOnlyRouter(SimpleRouter): """ - A router for read-only APIs, which doesn't use trailing suffixes. + A router for read-only APIs, which doesn't use trailing slashes. """ routes = [ - (r'^{prefix}$', {'get': 'list'}, '{basename}-list'), - (r'^{prefix}/{lookup}$', {'get': 'retrieve'}, '{basename}-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'}) ] +The `SimpleRouter` class provides another example of setting the `.routes` attribute. + ## 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 insect 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. +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/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index f8761cb2b..a3cd1d6ab 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -28,6 +28,8 @@ We'll declare a serializer that we can use to serialize and deserialize `Comment Declaring a serializer looks very similar to declaring a form: + from rest_framework import serializers + class CommentSerializer(serializers.Serializer): email = serializers.EmailField() content = serializers.CharField(max_length=200) @@ -59,6 +61,8 @@ We can now use `CommentSerializer` to serialize a comment, or list of comments. At this point we've translated the model instance into Python native datatypes. To finalise the serialization process we render the data into `json`. + from rest_framework.renderers import JSONRenderer + json = JSONRenderer().render(serializer.data) json # '{"email": "leila@example.com", "content": "foo bar", "created": "2012-08-22T16:20:09.822"}' @@ -67,6 +71,9 @@ At this point we've translated the model instance into Python native datatypes. Deserialization is similar. First we parse a stream into Python native datatypes... + from StringIO import StringIO + from rest_framework.parsers import JSONParser + stream = StringIO(json) data = JSONParser().parse(stream) @@ -177,7 +184,7 @@ If a nested representation may optionally accept the `None` value you should pas content = serializers.CharField(max_length=200) created = serializers.DateTimeField() -Similarly if a nested representation should be a list of items, you should the `many=True` flag to the nested serialized. +Similarly if a nested representation should be a list of items, you should pass the `many=True` flag to the nested serialized. class CommentSerializer(serializers.Serializer): user = UserSerializer(required=False) @@ -185,11 +192,13 @@ Similarly if a nested representation should be a list of items, you should the ` content = serializers.CharField(max_length=200) created = serializers.DateTimeField() ---- +Validation of nested objects will work the same as before. Errors with nested objects will be nested under the field name of the nested object. -**Note**: Nested serializers are only suitable for read-only representations, as there are cases where they would have ambiguous or non-obvious behavior if used when updating instances. For read-write representations you should always use a flat representation, by using one of the `RelatedField` subclasses. - ---- + serializer = CommentSerializer(comment, data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'}) + serializer.is_valid() + # False + serializer.errors + # {'user': {'email': [u'Enter a valid e-mail address.']}, 'created': [u'This field is required.']} ## Dealing with multiple objects @@ -241,7 +250,7 @@ This allows you to write views that update or create multiple items when a `PUT` serializer = BookSerializer(queryset, data=data, many=True) serializer.is_valid() # True - serialize.save() # `.save()` will be called on each updated or newly created instance. + serializer.save() # `.save()` will be called on each updated or newly created instance. By default bulk updates will be limited to updating instances that already exist in the provided queryset. @@ -253,7 +262,7 @@ When performing a bulk update you may want to allow new items to be created, and serializer.save() # `.save()` will be called on updated or newly created instances. # `.delete()` will be called on any other items in the `queryset`. -Passing `allow_add_remove=True` ensures that any update operations will completely overwrite the existing queryset, rather than simply updating existing objects. +Passing `allow_add_remove=True` ensures that any update operations will completely overwrite the existing queryset, rather than simply updating existing objects. #### How identity is determined when performing bulk updates @@ -293,8 +302,7 @@ You can provide arbitrary additional context by passing a `context` argument whe The context dictionary can be used within any serializer field logic, such as a custom `.to_native()` method, by accessing the `self.context` attribute. ---- - +- # ModelSerializer Often you'll want serializer classes that map closely to model definitions. @@ -308,6 +316,12 @@ By default, all the model fields on the class will be mapped to corresponding se Any relationships such as foreign keys on the model will be mapped to `PrimaryKeyRelatedField`. Other models fields will be mapped to a corresponding serializer field. +--- + +**Note**: When validation is applied to a `ModelSerializer`, both the serializer fields, and their corresponding model fields must correctly validate. If you have optional fields on your model, make sure to correctly set `blank=True` on the model field, as well as setting `required=False` on the serializer field. + +--- + ## Specifying which fields should be included If you only want a subset of the default fields to be used in a model serializer, you can do so using `fields` or `exclude` options, just as you would with a `ModelForm`. @@ -331,6 +345,8 @@ The default `ModelSerializer` uses primary keys for relationships, but you can a The `depth` option should be set to an integer value that indicates the depth of relationships that should be traversed before reverting to a flat representation. +If you want to customize the way the serialization is done (e.g. using `allow_add_remove`) you'll need to define the field yourself. + ## Specifying which fields should be read-only You may wish to specify multiple fields as read-only. Instead of adding each field explicitly with the `read_only=True` attribute, you may use the `read_only_fields` Meta option, like so: @@ -397,7 +413,7 @@ You can change the field that is used for object lookups by setting the `lookup_ Not that the `lookup_field` will be used as the default on *all* hyperlinked fields, including both the URL identity, and any hyperlinked relationships. -For more specfic requirements such as specifying a different lookup for each field, you'll want to set the fields on the serializer explicitly. For example: +For more specific requirements such as specifying a different lookup for each field, you'll want to set the fields on the serializer explicitly. For example: class AccountSerializer(serializers.HyperlinkedModelSerializer): url = serializers.HyperlinkedIdentityField( @@ -423,6 +439,49 @@ You can create customized subclasses of `ModelSerializer` or `HyperlinkedModelSe Doing so should be considered advanced usage, and will only be needed if you have some particular serializer requirements that you often need to repeat. +## Dynamically modifying fields + +Once a serializer has been initialized, the dictionary of fields that are set on the serializer may be accessed using the `.fields` attribute. Accessing and modifying this attribute allows you to dynamically modify the serializer. + +Modifying the `fields` 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. + +### Example + +For 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: + + class DynamicFieldsModelSerializer(serializers.ModelSerializer): + """ + A ModelSerializer that takes an additional `fields` argument that + controls which fields should be displayed. + """ + + def __init__(self, *args, **kwargs): + # Don't pass the 'fields' arg up to the superclass + fields = kwargs.pop('fields', None) + + # Instantiate the superclass normally + super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs) + + if fields: + # Drop any fields that are not specified in the `fields` argument. + allowed = set(fields) + existing = set(self.fields.keys()) + for field_name in existing - allowed: + self.fields.pop(field_name) + +This would then allow you to do the following: + + >>> class UserSerializer(DynamicFieldsModelSerializer): + >>> class Meta: + >>> model = User + >>> fields = ('id', 'username', 'email') + >>> + >>> print UserSerializer(user) + {'id': 2, 'username': 'jonwatts', 'email': 'jon@example.com'} + >>> + >>> print UserSerializer(user, fields=('id', 'email')) + {'id': 2, 'email': 'jon@example.com'} + ## Customising the default fields The `field_mapping` attribute is a dictionary that maps model classes to serializer classes. Overriding the attribute will let you set a different set of default serializer classes. @@ -457,7 +516,7 @@ Note that the `model_field` argument will be `None` for reverse relationships. Returns the field instance that should be used for non-relational, non-pk fields. -## Example +### Example The following custom model serializer could be used as a base class for model serializers that should always exclude the pk by default. diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 4a5164c9b..542e8c5fa 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -28,7 +28,7 @@ you should use the `api_settings` object. For example. print api_settings.DEFAULT_AUTHENTICATION_CLASSES -The `api_settings` object will check for any user-defined settings, and otherwise fallback to the default values. Any setting that uses string import paths to refer to a class will automatically import and return the referenced class, instead of the string literal. +The `api_settings` object will check for any user-defined settings, and otherwise fall back to the default values. Any setting that uses string import paths to refer to a class will automatically import and return the referenced class, instead of the string literal. --- @@ -127,6 +127,35 @@ Default: `None` The name of a query parameter, which can be used by the client to override the default page size to use for pagination. If set to `None`, clients may not override the default page size. +For example, given the following settings: + + REST_FRAMEWORK = { + 'PAGINATE_BY': 10, + 'PAGINATE_BY_PARAM': 'page_size', + } + +A client would be able to modify the pagination size by using the `page_size` query parameter. For example: + + GET http://example.com/api/accounts?page_size=25 + +Default: `None` + +#### MAX_PAGINATE_BY + +The maximum page size to allow when the page size is specified by the client. If set to `None`, then no maximum limit is applied. + +For example, given the following settings: + + REST_FRAMEWORK = { + 'PAGINATE_BY': 10, + 'PAGINATE_BY_PARAM': 'page_size', + 'MAX_PAGINATE_BY': 100 + } + +A client request like the following would return a paginated list of up to 100 items. + + GET http://example.com/api/accounts?page_size=999 + Default: `None` --- @@ -149,6 +178,33 @@ Default: `None` --- +## Test settings + +*The following settings control the behavior of APIRequestFactory and APIClient* + +#### TEST_REQUEST_DEFAULT_FORMAT + +The default format that should be used when making test requests. + +This should match up with the format of one of the renderer classes in the `TEST_REQUEST_RENDERER_CLASSES` setting. + +Default: `'multipart'` + +#### TEST_REQUEST_RENDERER_CLASSES + +The renderer classes that are supported when building test requests. + +The format of any of these renderer classes may be used when constructing a test request, for example: `client.post('/users', {'username': 'jamie'}, format='json')` + +Default: + + ( + 'rest_framework.renderers.MultiPartRenderer', + 'rest_framework.renderers.JSONRenderer' + ) + +--- + ## Browser overrides *The following settings provide URL or form-based overrides of the default browser behavior.* @@ -247,6 +303,40 @@ Default: `['iso-8601']` --- +## View names and descriptions + +**The following settings are used to generate the view names and descriptions, as used in responses to `OPTIONS` requests, and as used in the browsable API.** + +#### VIEW_NAME_FUNCTION + +A string representing the function that should be used when generating view names. + +This should be a function with the following signature: + + view_name(cls, suffix=None) + +* `cls`: The view class. Typically the name function would inspect the name of the class when generating a descriptive name, by accessing `cls.__name__`. +* `suffix`: The optional suffix used when differentiating individual views in a viewset. + +Default: `'rest_framework.views.get_view_name'` + +#### VIEW_DESCRIPTION_FUNCTION + +A string representing the function that should be used when generating view descriptions. + +This setting can be changed to support markup styles other than the default markdown. For example, you can use it to support `rst` markup in your view docstrings being output in the browsable API. + +This should be a function with the following signature: + + view_description(cls, html=False) + +* `cls`: The view class. Typically the description function would inspect the docstring of the class when generating a description, by accessing `cls.__doc__` +* `html`: A boolean indicating if HTML output is required. `True` when used in the browsable API, and `False` when used in generating `OPTIONS` responses. + +Default: `'rest_framework.views.get_view_description'` + +--- + ## Miscellaneous settings #### FORMAT_SUFFIX_KWARG diff --git a/docs/api-guide/status-codes.md b/docs/api-guide/status-codes.md index db2e059c3..409f659b2 100644 --- a/docs/api-guide/status-codes.md +++ b/docs/api-guide/status-codes.md @@ -9,6 +9,7 @@ Using bare status codes in your responses isn't recommended. REST framework includes a set of named constants that you can use to make more code more obvious and readable. from rest_framework import status + from rest_framework.response import Response def empty_view(self): content = {'please move along': 'nothing to see here'} diff --git a/docs/api-guide/testing.md b/docs/api-guide/testing.md new file mode 100644 index 000000000..35c1f7660 --- /dev/null +++ b/docs/api-guide/testing.md @@ -0,0 +1,272 @@ + + +# Testing + +> Code without tests is broken as designed. +> +> — [Jacob Kaplan-Moss][cite] + +REST framework includes a few helper classes that extend Django's existing test framework, and improve support for making API requests. + +# APIRequestFactory + +Extends [Django's existing `RequestFactory` class][requestfactory]. + +## Creating test requests + +The `APIRequestFactory` class supports an almost identical API to Django's standard `RequestFactory` class. This means the that standard `.get()`, `.post()`, `.put()`, `.patch()`, `.delete()`, `.head()` and `.options()` methods are all available. + + from rest_framework.test import APIRequestFactory + + # Using the standard RequestFactory API to create a form POST request + factory = APIRequestFactory() + request = factory.post('/notes/', {'title': 'new idea'}) + +#### Using the `format` argument + +Methods which create a request body, such as `post`, `put` and `patch`, include a `format` argument, which make it easy to generate requests using a content type other than multipart form data. For example: + + # Create a JSON POST request + factory = APIRequestFactory() + request = factory.post('/notes/', {'title': 'new idea'}, format='json') + +By default the available formats are `'multipart'` and `'json'`. For compatibility with Django's existing `RequestFactory` the default format is `'multipart'`. + +To support a wider set of request formats, or change the default format, [see the configuration section][configuration]. + +#### Explicitly encoding the request body + +If you need to explicitly encode the request body, you can do so by setting the `content_type` flag. For example: + + request = factory.post('/notes/', json.dumps({'title': 'new idea'}), content_type='application/json') + +#### PUT and PATCH with form data + +One difference worth noting between Django's `RequestFactory` and REST framework's `APIRequestFactory` is that multipart form data will be encoded for methods other than just `.post()`. + +For example, using `APIRequestFactory`, you can make a form PUT request like so: + + factory = APIRequestFactory() + request = factory.put('/notes/547/', {'title': 'remember to email dave'}) + +Using Django's `RequestFactory`, you'd need to explicitly encode the data yourself: + + from django.test.client import encode_multipart, RequestFactory + + factory = RequestFactory() + data = {'title': 'remember to email dave'} + content = encode_multipart('BoUnDaRyStRiNg', data) + content_type = 'multipart/form-data; boundary=BoUnDaRyStRiNg' + request = factory.put('/notes/547/', content, content_type=content_type) + +## Forcing authentication + +When testing views directly using a request factory, it's often convenient to be able to directly authenticate the request, rather than having to construct the correct authentication credentials. + +To forcibly authenticate a request, use the `force_authenticate()` method. + + factory = APIRequestFactory() + user = User.objects.get(username='olivia') + view = AccountDetail.as_view() + + # Make an authenticated request to the view... + request = factory.get('/accounts/django-superstars/') + force_authenticate(request, user=user) + response = view(request) + +The signature for the method is `force_authenticate(request, user=None, token=None)`. When making the call, either or both of the user and token may be set. + +For example, when forcibly authenticating using a token, you might do something like the following: + + user = User.objects.get(username='olivia') + request = factory.get('/accounts/django-superstars/') + force_authenticate(request, user=user, token=user.token) + +--- + +**Note**: When using `APIRequestFactory`, the object that is returned is Django's standard `HttpRequest`, and not REST framework's `Request` object, which is only generated once the view is called. + +This means that setting attributes directly on the request object may not always have the effect you expect. For example, setting `.token` directly will have no effect, and setting `.user` directly will only work if session authentication is being used. + + # Request will only authenticate if `SessionAuthentication` is in use. + request = factory.get('/accounts/django-superstars/') + request.user = user + response = view(request) + +--- + +## Forcing CSRF validation + +By default, requests created with `APIRequestFactory` will not have CSRF validation applied when passed to a REST framework view. If you need to explicitly turn CSRF validation on, you can do so by setting the `enforce_csrf_checks` flag when instantiating the factory. + + factory = APIRequestFactory(enforce_csrf_checks=True) + +--- + +**Note**: It's worth noting that Django's standard `RequestFactory` doesn't need to include this option, because when using regular Django the CSRF validation takes place in middleware, which is not run when testing views directly. When using REST framework, CSRF validation takes place inside the view, so the request factory needs to disable view-level CSRF checks. + +--- + +# APIClient + +Extends [Django's existing `Client` class][client]. + +## Making requests + +The `APIClient` class supports the same request interface as `APIRequestFactory`. This means the that standard `.get()`, `.post()`, `.put()`, `.patch()`, `.delete()`, `.head()` and `.options()` methods are all available. For example: + + from rest_framework.test import APIClient + + client = APIClient() + client.post('/notes/', {'title': 'new idea'}, format='json') + +To support a wider set of request formats, or change the default format, [see the configuration section][configuration]. + +## Authenticating + +#### .login(**kwargs) + +The `login` method functions exactly as it does with Django's regular `Client` class. This allows you to authenticate requests against any views which include `SessionAuthentication`. + + # Make all requests in the context of a logged in session. + client = APIClient() + client.login(username='lauren', password='secret') + +To logout, call the `logout` method as usual. + + # Log out + client.logout() + +The `login` method is appropriate for testing APIs that use session authentication, for example web sites which include AJAX interaction with the API. + +#### .credentials(**kwargs) + +The `credentials` method can be used to set headers that will then be included on all subsequent requests by the test client. + + from rest_framework.authtoken.models import Token + from rest_framework.test import APIClient + + # Include an appropriate `Authorization:` header on all requests. + token = Token.objects.get(user__username='lauren') + client = APIClient() + client.credentials(HTTP_AUTHORIZATION='Token ' + token.key) + +Note that calling `credentials` a second time overwrites any existing credentials. You can unset any existing credentials by calling the method with no arguments. + + # Stop including any credentials + client.credentials() + +The `credentials` method is appropriate for testing APIs that require authentication headers, such as basic authentication, OAuth1a and OAuth2 authentication, and simple token authentication schemes. + +#### .force_authenticate(user=None, token=None) + +Sometimes you may want to bypass authentication, and simple force all requests by the test client to be automatically treated as authenticated. + +This can be a useful shortcut if you're testing the API but don't want to have to construct valid authentication credentials in order to make test requests. + + user = User.objects.get(username='lauren') + client = APIClient() + client.force_authenticate(user=user) + +To unauthenticate subsequent requests, call `force_authenticate` setting the user and/or token to `None`. + + client.force_authenticate(user=None) + +## CSRF validation + +By default CSRF validation is not applied when using `APIClient`. If you need to explicitly enable CSRF validation, you can do so by setting the `enforce_csrf_checks` flag when instantiating the client. + + client = APIClient(enforce_csrf_checks=True) + +As usual CSRF validation will only apply to any session authenticated views. This means CSRF validation will only occur if the client has been logged in by calling `login()`. + +--- + +# Test cases + +REST framework includes the following test case classes, that mirror the existing Django test case classes, but use `APIClient` instead of Django's default `Client`. + +* `APISimpleTestCase` +* `APITransactionTestCase` +* `APITestCase` +* `APILiveServerTestCase` + +## Example + +You can use any of REST framework's test case classes as you would for the regular Django test case classes. The `self.client` attribute will be an `APIClient` instance. + + from django.core.urlresolvers import reverse + from rest_framework import status + from rest_framework.test import APITestCase + + class AccountTests(APITestCase): + def test_create_account(self): + """ + Ensure we can create a new account object. + """ + url = reverse('account-list') + expected = {'name': 'DabApps'} + response = self.client.post(url, data, format='json') + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.data, expected) + +--- + +# Testing responses + +## Checking the response data + +When checking the validity of test responses it's often more convenient to inspect the data that the response was created with, rather than inspecting the fully rendered response. + +For example, it's easier to inspect `request.data`: + + response = self.client.get('/users/4/') + self.assertEqual(response.data, {'id': 4, 'username': 'lauren'}) + +Instead of inspecting the result of parsing `request.content`: + + response = self.client.get('/users/4/') + self.assertEqual(json.loads(response.content), {'id': 4, 'username': 'lauren'}) + +## Rendering responses + +If you're testing views directly using `APIRequestFactory`, the responses that are returned will not yet be rendered, as rendering of template responses is performed by Django's internal request-response cycle. In order to access `response.content`, you'll first need to render the response. + + view = UserDetail.as_view() + request = factory.get('/users/4') + response = view(request, pk='4') + response.render() # Cannot access `response.content` without this. + self.assertEqual(response.content, '{"username": "lauren", "id": 4}') + +--- + +# Configuration + +## Setting the default format + +The default format used to make test requests may be set using the `TEST_REQUEST_DEFAULT_FORMAT` setting key. For example, to always use JSON for test requests by default instead of standard multipart form requests, set the following in your `settings.py` file: + + REST_FRAMEWORK = { + ... + 'TEST_REQUEST_DEFAULT_FORMAT': 'json' + } + +## Setting the available formats + +If you need to test requests using something other than multipart or json requests, you can do so by setting the `TEST_REQUEST_RENDERER_CLASSES` setting. + +For example, to add support for using `format='yaml'` in test requests, you might have something like this in your `settings.py` file. + + REST_FRAMEWORK = { + ... + 'TEST_REQUEST_RENDERER_CLASSES': ( + 'rest_framework.renderers.MultiPartRenderer', + 'rest_framework.renderers.JSONRenderer', + 'rest_framework.renderers.YAMLRenderer' + ) + } + +[cite]: http://jacobian.org/writing/django-apps-with-buildout/#s-create-a-test-wrapper +[client]: https://docs.djangoproject.com/en/dev/topics/testing/overview/#module-django.test.client +[requestfactory]: https://docs.djangoproject.com/en/dev/topics/testing/advanced/#django.test.client.RequestFactory +[configuration]: #configuration diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index d6de85ba8..cc4692171 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -12,7 +12,7 @@ As with permissions, multiple throttles may be used. Your API might have a rest Another scenario where you might want to use multiple throttles would be if you need to impose different constraints on different parts of the API, due to some services being particularly resource-intensive. -Multiple throttles can also be used if you want to impose both burst throttling rates, and sustained throttling rates. For example, you might want to limit a user to a maximum of 60 requests per minute, and 1000 requests per day. +Multiple throttles can also be used if you want to impose both burst throttling rates, and sustained throttling rates. For example, you might want to limit a user to a maximum of 60 requests per minute, and 1000 requests per day. Throttles do not necessarily only refer to rate-limiting requests. For example a storage service might also need to throttle against bandwidth, and a paid data service might want to throttle against a certain number of a records being accessed. @@ -43,8 +43,12 @@ The rate descriptions used in `DEFAULT_THROTTLE_RATES` may include `second`, `mi You can also set the throttling policy on a per-view or per-viewset basis, using the `APIView` class based views. + from rest_framework.response import Response + from rest_framework.throttling import UserRateThrottle + from rest_framework.views import APIView + class ExampleView(APIView): - throttle_classes = (UserThrottle,) + throttle_classes = (UserRateThrottle,) def get(self, request, format=None): content = { @@ -55,7 +59,7 @@ using the `APIView` class based views. Or, if you're using the `@api_view` decorator with function based views. @api_view('GET') - @throttle_classes(UserThrottle) + @throttle_classes(UserRateThrottle) def example_view(request, format=None): content = { 'status': 'request was permitted' @@ -66,28 +70,35 @@ Or, if you're using the `@api_view` decorator with function based views. The throttle classes provided by REST framework use Django's cache backend. You should make sure that you've set appropriate [cache settings][cache-setting]. The default value of `LocMemCache` backend should be okay for simple setups. See Django's [cache documentation][cache-docs] 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: + + class CustomAnonRateThrottle(AnonRateThrottle): + cache = get_cache('alternate') + +You'll need to rememeber to also set your custom throttle class in the `'DEFAULT_THROTTLE_CLASSES'` settings key, or using the `throttle_classes` view attribute. + --- # API Reference ## AnonRateThrottle -The `AnonThrottle` will only ever throttle unauthenticated users. The IP address of the incoming request is used to generate a unique key to throttle against. +The `AnonRateThrottle` will only ever throttle unauthenticated users. The IP address of the incoming request is used to generate a unique key to throttle against. The allowed request rate is determined from one of the following (in order of preference). -* The `rate` property on the class, which may be provided by overriding `AnonThrottle` and setting the property. +* The `rate` property on the class, which may be provided by overriding `AnonRateThrottle` and setting the property. * The `DEFAULT_THROTTLE_RATES['anon']` setting. -`AnonThrottle` is suitable if you want to restrict the rate of requests from unknown sources. +`AnonRateThrottle` is suitable if you want to restrict the rate of requests from unknown sources. ## UserRateThrottle -The `UserThrottle` will throttle users to a given rate of requests across the API. The user id is used to generate a unique key to throttle against. Unauthenticated requests will fall back to using the IP address of the incoming request to generate a unique key to throttle against. +The `UserRateThrottle` will throttle users to a given rate of requests across the API. The user id is used to generate a unique key to throttle against. Unauthenticated requests will fall back to using the IP address of the incoming request to generate a unique key to throttle against. The allowed request rate is determined from one of the following (in order of preference). -* The `rate` property on the class, which may be provided by overriding `UserThrottle` and setting the property. +* The `rate` property on the class, which may be provided by overriding `UserRateThrottle` and setting the property. * The `DEFAULT_THROTTLE_RATES['user']` setting. An API may have multiple `UserRateThrottles` in place at the same time. To do so, override `UserRateThrottle` and set a unique "scope" for each class. @@ -113,11 +124,11 @@ For example, multiple user throttle rates could be implemented by using the foll } } -`UserThrottle` is suitable if you want simple global rate restrictions per-user. +`UserRateThrottle` is suitable if you want simple global rate restrictions per-user. ## ScopedRateThrottle -The `ScopedThrottle` class can be used to restrict access to specific parts of the API. This throttle will only be applied if the view that is being accessed includes a `.throttle_scope` property. The unique throttle key will then be formed by concatenating the "scope" of the request with the unique user id or IP address. +The `ScopedRateThrottle` class can be used to restrict access to specific parts of the API. This throttle will only be applied if the view that is being accessed includes a `.throttle_scope` property. The unique throttle key will then be formed by concatenating the "scope" of the request with the unique user id or IP address. The allowed request rate is determined by the `DEFAULT_THROTTLE_RATES` setting using a key from the request "scope". diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index 37ebd55f5..15581e098 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -110,7 +110,7 @@ You won't typically need to override this method. ### .finalize_response(self, request, response, \*args, **kwargs) -Ensures that any `Response` object returned from the handler method will be rendered into the correct content type, as determined by the content negotation. +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. @@ -137,11 +137,11 @@ The core of this functionality is the `api_view` decorator, which takes a list o return Response({"message": "Hello, world!"}) -This view will use the default renderers, parsers, authentication classes etc specified in the [settings](settings). +This view will use the default renderers, parsers, authentication classes etc specified in the [settings]. ## API policy decorators -To override the default settings, REST framework provides a set of additional decorators which can be added to your views. These must come *after* (below) the `@api_view` decorator. For example, to create a view that uses a [throttle](throttling) to ensure it can only be called once per day by a particular user, use the `@throttle_classes` decorator, passing a list of throttle classes: +To override the default settings, REST framework provides a set of additional decorators which can be added to your views. These must come *after* (below) the `@api_view` decorator. For example, to create a view that uses a [throttle][throttling] to ensure it can only be called once per day by a particular user, use the `@throttle_classes` decorator, passing a list of throttle classes: from rest_framework.decorators import api_view, throttle_classes from rest_framework.throttling import UserRateThrottle diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md index 79257e2af..2e65b7a43 100644 --- a/docs/api-guide/viewsets.md +++ b/docs/api-guide/viewsets.md @@ -19,6 +19,12 @@ Typically, rather than explicitly registering the views in a viewset in the urlc 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 + from django.shortcuts import get_object_or_404 + from myapps.serializers import UserSerializer + from rest_framework import viewsets + from rest_framewor.responses import Response + class UserViewSet(viewsets.ViewSet): """ A simple ViewSet that for listing or retrieving users. @@ -27,7 +33,7 @@ Let's define a simple viewset that can be used to list or retrieve all the users queryset = User.objects.all() serializer = UserSerializer(queryset, many=True) return Response(serializer.data) - + def retrieve(self, request, pk=None): queryset = User.objects.all() user = get_object_or_404(queryset, pk=pk) @@ -41,6 +47,9 @@ If we need to, we can bind this viewset into two separate views, like so: 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 + from rest_framework.routers import DefaultRouter + router = DefaultRouter() router.register(r'users', UserViewSet) urlpatterns = router.urls @@ -69,7 +78,7 @@ The default routers included with REST framework will provide routes for a stand """ Example empty viewset demonstrating the standard actions that will be handled by a router class. - + If you're using format suffixes, make sure to also include the `format=None` keyword argument for each action. """ @@ -98,17 +107,19 @@ 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 myapp.serializers import UserSerializer + from rest_framework.response import Response + from myapp.serializers import UserSerializer, PasswordSerializer class UserViewSet(viewsets.ModelViewSet): """ - A viewset that provides the standard actions + A viewset that provides the standard actions """ queryset = User.objects.all() serializer_class = UserSerializer - - @action + + @action() def set_password(self, request, pk=None): user = self.get_object() serializer = PasswordSerializer(data=request.DATA) @@ -131,6 +142,10 @@ The `@action` decorator will route `POST` requests by default, but may also acce @action(methods=['POST', 'DELETE']) def unset_password(self, request, pk=None): ... + +The two new actions will then be available at the urls `^users/{pk}/set_password/$` and `^users/{pk}/unset_password/$` + + --- # API Reference @@ -176,7 +191,7 @@ Note that you can use any of the standard attributes or method overrides provide permission_classes = [IsAccountAdminOrReadOnly] def get_queryset(self): - return request.user.accounts.all() + return self.request.user.accounts.all() 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. @@ -197,7 +212,7 @@ As with `ModelViewSet`, you'll normally need to provide at least the `queryset` Again, as with `ModelViewSet`, you can use any of the standard attributes and method overrides available to `GenericAPIView`. -# Custom ViewSet base classes +# Custom ViewSet base classes 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. @@ -205,13 +220,13 @@ You may need to provide custom `ViewSet` classes that do not have the full set o To create a base viewset class that provides `create`, `list` and `retrieve` operations, inherit from `GenericViewSet`, and mixin the required actions: - class CreateListRetrieveViewSet(mixins.CreateMixin, - mixins.ListMixin, - mixins.RetrieveMixin, + class CreateListRetrieveViewSet(mixins.CreateModelMixin, + mixins.ListModelMixin, + mixins.RetrieveModelMixin, viewsets.GenericViewSet): """ A viewset that provides `retrieve`, `update`, and `list` actions. - + To use it, override the class and set the `.queryset` and `.serializer_class` attributes. """ diff --git a/docs/css/default.css b/docs/css/default.css index a4f05daa8..af6a9cc03 100644 --- a/docs/css/default.css +++ b/docs/css/default.css @@ -303,3 +303,7 @@ table { border-color: white; margin-bottom: 0.6em; } + +.side-nav { + overflow-y: scroll; +} diff --git a/docs/img/apiary.png b/docs/img/apiary.png new file mode 100644 index 000000000..923d384eb Binary files /dev/null and b/docs/img/apiary.png differ diff --git a/docs/img/autocomplete.png b/docs/img/autocomplete.png new file mode 100644 index 000000000..29075b257 Binary files /dev/null and b/docs/img/autocomplete.png differ diff --git a/docs/img/cerulean.png b/docs/img/cerulean.png new file mode 100644 index 000000000..e647d5e81 Binary files /dev/null and b/docs/img/cerulean.png differ diff --git a/docs/img/django-rest-swagger.png b/docs/img/django-rest-swagger.png new file mode 100644 index 000000000..96a6b2380 Binary files /dev/null and b/docs/img/django-rest-swagger.png differ diff --git a/docs/img/rest-framework-docs.png b/docs/img/rest-framework-docs.png new file mode 100644 index 000000000..736a00955 Binary files /dev/null and b/docs/img/rest-framework-docs.png differ diff --git a/docs/img/self-describing.png b/docs/img/self-describing.png new file mode 100644 index 000000000..ecbe4fe40 Binary files /dev/null and b/docs/img/self-describing.png differ diff --git a/docs/img/slate.png b/docs/img/slate.png new file mode 100644 index 000000000..31644eafe Binary files /dev/null and b/docs/img/slate.png differ diff --git a/docs/index.md b/docs/index.md index b04e23465..e0a2e911b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -15,11 +15,11 @@ Django REST framework is a powerful and flexible toolkit that makes it easy to b Some reasons you might want to use REST framework: -* The Web browseable API is a huge usability win for your developers. -* Authentication policies including OAuth1a and OAuth2 out of the box. -* Serialization that supports both ORM and non-ORM data sources. -* Customizable all the way down - just use regular function-based views if you don't need the more powerful features. -* Extensive documentation, and great community support. +* The [Web browseable API][sandbox] is a huge usability win for your developers. +* [Authentication policies][authentication] including [OAuth1a][oauth1-section] and [OAuth2][oauth2-section] out of the box. +* [Serialization][serializers] that supports both [ORM][modelserializer-section] and [non-ORM][serializer-section] data sources. +* Customizable all the way down - just use [regular function-based views][functionview-section] if you don't need the [more][generic-views] [powerful][viewsets] [features][routers]. +* [Extensive documentation][index], and [great community support][group]. There is a live example API for testing purposes, [available here][sandbox]. @@ -164,12 +164,14 @@ The API guide is your complete reference manual to all the functionality provide * [Returning URLs][reverse] * [Exceptions][exceptions] * [Status codes][status] +* [Testing][testing] * [Settings][settings] ## Topics General guides to using REST framework. +* [Documenting your API][documenting-your-api] * [AJAX, CSRF & CORS][ajax-csrf-cors] * [Browser enhancements][browser-enhancements] * [The Browsable API][browsableapi] @@ -198,7 +200,7 @@ To run the tests against all supported configurations, first install [the tox te ## Support -For support please see the [REST framework discussion group][group], try the `#restframework` channel on `irc.freenode.net`, or raise a question on [Stack Overflow][stack-overflow], making sure to include the ['django-rest-framework'][django-rest-framework-tag] tag. +For support please see the [REST framework discussion group][group], try the `#restframework` channel on `irc.freenode.net`, search [the IRC archives][botbot], or raise a question on [Stack Overflow][stack-overflow], making sure to include the ['django-rest-framework'][django-rest-framework-tag] tag. [Paid support is available][paid-support] from [DabApps][dabapps], and can include work on REST framework core, or support with building your REST framework API. Please [contact DabApps][contact-dabapps] if you'd like to discuss commercial support options. @@ -250,6 +252,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [django-oauth2-provider]: https://github.com/caffeinehit/django-oauth2-provider [0.4]: https://github.com/tomchristie/django-rest-framework/tree/0.4.X [image]: img/quickstart.png +[index]: . +[oauth1-section]: api-guide/authentication.html#oauthauthentication +[oauth2-section]: api-guide/authentication.html#oauth2authentication +[serializer-section]: api-guide/serializers.html#serializers +[modelserializer-section]: api-guide/serializers.html#modelserializer +[functionview-section]: api-guide/views.html#function-based-views [sandbox]: http://restframework.herokuapp.com/ [quickstart]: tutorial/quickstart.md @@ -281,8 +289,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [reverse]: api-guide/reverse.md [exceptions]: api-guide/exceptions.md [status]: api-guide/status-codes.md +[testing]: api-guide/testing.md [settings]: api-guide/settings.md +[documenting-your-api]: topics/documenting-your-api.md [ajax-csrf-cors]: topics/ajax-csrf-cors.md [browser-enhancements]: topics/browser-enhancements.md [browsableapi]: topics/browsable-api.md @@ -297,6 +307,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [tox]: http://testrun.org/tox/latest/ [group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework +[botbot]: https://botbot.me/freenode/restframework/ [stack-overflow]: http://stackoverflow.com/ [django-rest-framework-tag]: http://stackoverflow.com/questions/tagged/django-rest-framework [django-tag]: http://stackoverflow.com/questions/tagged/django diff --git a/docs/template.html b/docs/template.html index 53656e7d4..a20c81110 100644 --- a/docs/template.html +++ b/docs/template.html @@ -89,12 +89,14 @@
  • Returning URLs
  • Exceptions
  • Status codes
  • +
  • Testing
  • Settings