diff options
Diffstat (limited to 'docs/api-guide')
| -rw-r--r-- | docs/api-guide/authentication.md | 48 | ||||
| -rw-r--r-- | docs/api-guide/fields.md | 193 | ||||
| -rw-r--r-- | docs/api-guide/filtering.md | 178 | ||||
| -rw-r--r-- | docs/api-guide/generic-views.md | 62 | ||||
| -rw-r--r-- | docs/api-guide/pagination.md | 35 | ||||
| -rw-r--r-- | docs/api-guide/parsers.md | 16 | ||||
| -rw-r--r-- | docs/api-guide/permissions.md | 16 | ||||
| -rw-r--r-- | docs/api-guide/relations.md | 139 | ||||
| -rw-r--r-- | docs/api-guide/renderers.md | 78 | ||||
| -rw-r--r-- | docs/api-guide/serializers.md | 142 | ||||
| -rw-r--r-- | docs/api-guide/settings.md | 34 | ||||
| -rw-r--r-- | docs/api-guide/status-codes.md | 2 | ||||
| -rw-r--r-- | docs/api-guide/throttling.md | 14 | ||||
| -rw-r--r-- | docs/api-guide/views.md | 50 |
14 files changed, 816 insertions, 191 deletions
diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 7bad4867..afd9a261 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -30,7 +30,7 @@ The default authentication policy may be set globally, using the `DEFAULT_AUTHEN REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( - 'rest_framework.authentication.UserBasicAuthentication', + 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', ) } @@ -38,7 +38,7 @@ The default authentication policy may be set globally, using the `DEFAULT_AUTHEN You can also set the authentication policy on a per-view basis, using the `APIView` class based views. class ExampleView(APIView): - authentication_classes = (SessionAuthentication, UserBasicAuthentication) + authentication_classes = (SessionAuthentication, BasicAuthentication) permission_classes = (IsAuthenticated,) def get(self, request, format=None): @@ -50,9 +50,9 @@ You can also set the authentication policy on a per-view basis, using the `APIVi Or, if you're using the `@api_view` decorator with function based views. - @api_view(('GET',)), - @authentication_classes((SessionAuthentication, UserBasicAuthentication)) - @permissions_classes((IsAuthenticated,)) + @api_view(['GET']) + @authentication_classes((SessionAuthentication, BasicAuthentication)) + @permission_classes((IsAuthenticated,)) def example_view(request, format=None): content = { 'user': unicode(request.user), # `django.contrib.auth.User` instance. @@ -68,7 +68,7 @@ This policy uses [HTTP Basic Authentication][basicauth], signed against a user's If successfully authenticated, `BasicAuthentication` provides the following credentials. -* `request.user` will be a `django.contrib.auth.models.User` instance. +* `request.user` will be a Django `User` instance. * `request.auth` will be `None`. **Note:** If you use `BasicAuthentication` in production you must ensure that your API is only available over `https` only. You should also ensure that your API clients will always re-request the username and password at login, and will never store those details to persistent storage. @@ -92,19 +92,38 @@ For clients to authenticate, the token key should be included in the `Authorizat If successfully authenticated, `TokenAuthentication` provides the following credentials. -* `request.user` will be a `django.contrib.auth.models.User` instance. +* `request.user` will be a Django `User` instance. * `request.auth` will be a `rest_framework.tokenauth.models.BasicToken` instance. **Note:** If you use `TokenAuthentication` in production you must ensure that your API is only available over `https` only. -## OAuthAuthentication +If you want every user to have an automatically generated Token, you can simply catch the User's `post_save` signal. -This policy uses the [OAuth 2.0][oauth] protocol to authenticate requests. OAuth is appropriate for server-server setups, such as when you want to allow a third-party service to access your API on a user's behalf. + @receiver(post_save, sender=User) + def create_auth_token(sender, instance=None, created=False, **kwargs): + if created: + Token.objects.create(user=instance) -If successfully authenticated, `OAuthAuthentication` provides the following credentials. +If you've already created some users, you can generate tokens for all existing users like this: -* `request.user` will be a `django.contrib.auth.models.User` instance. -* `request.auth` will be a `rest_framework.models.OAuthToken` instance. + from django.contrib.auth.models import User + from rest_framework.authtoken.models import Token + + for user in User.objects.all(): + Token.objects.get_or_create(user=user) + +When using `TokenAuthentication`, you may want to provide a mechanism for clients to obtain a token given the username and password. +REST framework provides a built-in view to provide this behavior. To use it, add the `obtain_auth_token` view to your URLconf: + + urlpatterns += patterns('', + url(r'^api-token-auth/', 'rest_framework.authtoken.views.obtain_auth_token') + ) + +Note that the URL part of the pattern can be whatever you want to use. + +The `obtain_auth_token` view will return a JSON response when valid `username` and `password` fields are POSTed to the view using form data or JSON: + + { 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' } ## SessionAuthentication @@ -112,9 +131,11 @@ This policy uses Django's default session backend for authentication. Session a If successfully authenticated, `SessionAuthentication` provides the following credentials. -* `request.user` will be a `django.contrib.auth.models.User` instance. +* `request.user` will be a Django `User` instance. * `request.auth` will be `None`. +If you're using an AJAX style API with SessionAuthentication, you'll need to make sure you include a valid CSRF token for any "unsafe" HTTP method calls, such as `PUT`, `POST` or `DELETE` requests. See the [Django CSRF documentation][csrf-ajax] for more details. + # Custom authentication To implement a custom authentication policy, subclass `BaseAuthentication` and override the `.authenticate(self, request)` method. The method should return a two-tuple of `(user, auth)` if authentication succeeds, or `None` otherwise. @@ -124,3 +145,4 @@ To implement a custom authentication policy, subclass `BaseAuthentication` and o [oauth]: http://oauth.net/2/ [permission]: permissions.md [throttling]: throttling.md +[csrf-ajax]: https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 7e117df7..5bc8f7f7 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -2,11 +2,11 @@ # Serializer fields -> Flat is better than nested. +> Each field in a Form class is responsible not only for validating data, but also for "cleaning" it -- normalizing it to a consistent format. > -> — [The Zen of Python][cite] +> — [Django documentation][cite] -Serializer fields handle converting between primative values and internal datatypes. They also deal with validating input values, as well as retrieving and setting the values from their parent objects. +Serializer fields handle converting between primitive values and internal datatypes. They also deal with validating input values, as well as retrieving and setting the values from their parent objects. --- @@ -14,6 +14,51 @@ Serializer fields handle converting between primative values and internal dataty --- +## Core arguments + +Each serializer field class constructor takes at least these arguments. Some Field classes take additional, field-specific arguments, but the following should always be accepted: + +### `source` + +The name of the attribute that will be used to populate the field. May be a method that only takes a `self` argument, such as `Field(source='get_absolute_url')`, or may use dotted notation to traverse attributes, such as `Field(source='user.email')`. + +The value `source='*'` has a special meaning, and is used to indicate that the entire object should be passed through to the field. This can be useful for creating nested representations. (See the implementation of the `PaginationSerializer` class for an example.) + +Defaults to the name of the field. + +### `read_only` + +Set this to `True` to ensure that the field is used when serializing a representation, but is not used when updating an instance during deserialization. + +Defaults to `False` + +### `required` + +Normally an error will be raised if a field is not supplied during deserialization. +Set to false if this field is not required to be present during deserialization. + +Defaults to `True`. + +### `default` + +If set, this gives the default value that will be used for the field if none is supplied. If not set the default behavior is to not populate the attribute at all. + +### `validators` + +A list of Django validators that should be used to validate deserialized values. + +### `error_messages` + +A dictionary of error codes to error messages. + +### `widget` + +Used only if rendering the field to HTML. +This argument sets the widget that should be used to render the field. + + +--- + # Generic Fields These generic fields are used for representing arbitrary model fields or the output of model methods. @@ -51,9 +96,9 @@ Would produce output similar to: 'expired': True } -By default, the `Field` class will perform a basic translation of the source value into primative datatypes, falling back to unicode representations of complex datatypes when necessary. +By default, the `Field` class will perform a basic translation of the source value into primitive datatypes, falling back to unicode representations of complex datatypes when necessary. -You can customize this behaviour by overriding the `.to_native(self, value)` method. +You can customize this behavior by overriding the `.to_native(self, value)` method. ## WritableField @@ -65,6 +110,24 @@ A generic field that can be tied to any arbitrary model field. The `ModelField` **Signature:** `ModelField(model_field=<Django ModelField class>)` +## SerializerMethodField + +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 + + class UserSerializer(serializers.ModelSerializer): + + days_since_joined = serializers.SerializerMethodField('get_days_since_joined') + + class Meta: + model = User + + def get_days_since_joined(self, obj): + return (now() - obj.date_joined).days + --- # Typed Fields @@ -86,6 +149,18 @@ or `django.db.models.fields.TextField`. **Signature:** `CharField(max_length=None, min_length=None)` +## URLField + +Corresponds to `django.db.models.fields.URLField`. Uses Django's `django.core.validators.URLValidator` for validation. + +**Signature:** `CharField(max_length=200, min_length=None)` + +## SlugField + +Corresponds to `django.db.models.fields.SlugField`. + +**Signature:** `CharField(max_length=50, min_length=None)` + ## ChoiceField A field that can accept a value out of a limited set of choices. @@ -96,6 +171,16 @@ A text representation, validates the text to be a valid e-mail address. Corresponds to `django.db.models.fields.EmailField` +## RegexField + +A text representation, that validates the given value matches against a certain regular expression. + +Uses Django's `django.core.validators.RegexValidator` for validation. + +Corresponds to `django.forms.fields.RegexField` + +**Signature:** `RegexField(regex, max_length=None, min_length=None)` + ## DateField A date representation. @@ -120,96 +205,32 @@ A floating point representation. Corresponds to `django.db.models.fields.FloatField`. ---- - -# Relational Fields +## FileField -Relational fields are used to represent model relationships. They can be applied to `ForeignKey`, `ManyToManyField` and `OneToOneField` relationships, as well as to reverse relationships, and custom relationships such as `GenericForeignKey`. +A file representation. Performs Django's standard FileField validation. -## RelatedField +Corresponds to `django.forms.fields.FileField`. -This field can be applied to any of the following: +**Signature:** `FileField(max_length=None, allow_empty_file=False)` -* A `ForeignKey` field. -* A `OneToOneField` field. -* A reverse OneToOne relationship -* Any other "to-one" relationship. + - `max_length` designates the maximum length for the file name. + + - `allow_empty_file` designates if empty files are allowed. -By default `RelatedField` will represent the target of the field using it's `__unicode__` method. +## ImageField -You can customise this behaviour by subclassing `ManyRelatedField`, and overriding the `.to_native(self, value)` method. +An image representation. -## ManyRelatedField +Corresponds to `django.forms.fields.ImageField`. -This field can be applied to any of the following: - -* A `ManyToManyField` field. -* A reverse ManyToMany relationship. -* A reverse ForeignKey relationship -* Any other "to-many" relationship. +Requires the `PIL` package. -By default `ManyRelatedField` will represent the targets of the field using their `__unicode__` method. +Signature and validation is the same as with `FileField`. -For example, given the following models: - - class TaggedItem(models.Model): - """ - Tags arbitrary model instances using a generic relation. - - See: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/ - """ - tag = models.SlugField() - content_type = models.ForeignKey(ContentType) - object_id = models.PositiveIntegerField() - content_object = GenericForeignKey('content_type', 'object_id') - - def __unicode__(self): - return self.tag - - - class Bookmark(models.Model): - """ - A bookmark consists of a URL, and 0 or more descriptive tags. - """ - url = models.URLField() - tags = GenericRelation(TaggedItem) - -And a model serializer defined like this: - - class BookmarkSerializer(serializers.ModelSerializer): - tags = serializers.ManyRelatedField(source='tags') - - class Meta: - model = Bookmark - exclude = ('id',) - -Then an example output format for a Bookmark instance would be: - - { - 'tags': [u'django', u'python'], - 'url': u'https://www.djangoproject.com/' - } - -## PrimaryKeyRelatedField - -As with `RelatedField` field can be applied to any "to-one" relationship, such as a `ForeignKey` field. - -`PrimaryKeyRelatedField` will represent the target of the field using it's primary key. - -Be default, `PrimaryKeyRelatedField` is read-write, although you can change this behaviour using the `readonly` flag. - -## ManyPrimaryKeyRelatedField - -As with `RelatedField` field can be applied to any "to-many" relationship, such as a `ManyToManyField` field, or a reverse `ForeignKey` relationship. - -`PrimaryKeyRelatedField` will represent the target of the field using their primary key. - -Be default, `ManyPrimaryKeyRelatedField` is read-write, although you can change this behaviour using the `readonly` flag. - -## HyperlinkedRelatedField - -## ManyHyperlinkedRelatedField +--- -## HyperLinkedIdentityField +**Note:** `FileFields` and `ImageFields` are only suitable for use with MultiPartParser, since e.g. json doesn't support file uploads. +Django's regular [FILE_UPLOAD_HANDLERS] are used for handling uploaded files. -[cite]: http://www.python.org/dev/peps/pep-0020/ +[cite]: https://docs.djangoproject.com/en/dev/ref/forms/api/#django.forms.Form.cleaned_data +[FILE_UPLOAD_HANDLERS]: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FILE_UPLOAD_HANDLERS diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md new file mode 100644 index 00000000..53ea7cbc --- /dev/null +++ b/docs/api-guide/filtering.md @@ -0,0 +1,178 @@ +<a class="github" href="filters.py"></a> + +# Filtering + +> The root QuerySet provided by the Manager describes all objects in the database table. Usually, though, you'll need to select only a subset of the complete set of objects. +> +> — [Django documentation][cite] + +The default behavior of REST framework's generic list views is to return the entire queryset for a model manager. Often you will want your API to restrict the items that are returned by the queryset. + +The simplest way to filter the queryset of any view that subclasses `MultipleObjectAPIView` is to override the `.get_queryset()` method. + +Overriding this method allows you to customize the queryset returned by the view in a number of different ways. + +## Filtering against the current user + +You might want to filter the queryset to ensure that only results relevant to the currently authenticated user making the request are returned. + +You can do so by filtering based on the value of `request.user`. + +For example: + + class PurchaseList(generics.ListAPIView) + model = Purchase + serializer_class = PurchaseSerializer + + def get_queryset(self): + """ + This view should return a list of all the purchases + for the currently authenticated user. + """ + user = self.request.user + return Purchase.objects.filter(purchaser=user) + + +## Filtering against the URL + +Another style of filtering might involve restricting the queryset based on some part of the URL. + +For example if your URL config contained an entry like this: + + url('^purchases/(?P<username>.+)/$', PurchaseList.as_view()), + +You could then write a view that returned a purchase queryset filtered by the username portion of the URL: + + class PurchaseList(generics.ListAPIView) + model = Purchase + serializer_class = PurchaseSerializer + + def get_queryset(self): + """ + This view should return a list of all the purchases for + the user as determined by the username portion of the URL. + """ + username = self.kwargs['username'] + return Purchase.objects.filter(purchaser__username=username) + +## Filtering against query parameters + +A final example of filtering the initial queryset would be to determine the initial queryset based on query parameters in the url. + +We can override `.get_queryset()` to deal with URLs such as `http://example.com/api/purchases?username=denvercoder9`, and filter the queryset only if the `username` parameter is included in the URL: + + class PurchaseList(generics.ListAPIView) + model = Purchase + serializer_class = PurchaseSerializer + + def get_queryset(self): + """ + Optionally restricts the returned purchases to a given user, + by filtering against a `username` query parameter in the URL. + """ + queryset = Purchase.objects.all() + username = self.request.QUERY_PARAMS.get('username', None) + if username is not None: + queryset = queryset.filter(purchaser__username=username) + return queryset + +--- + +# Generic Filtering + +As well as being able to override the default queryset, REST framework also includes support for generic filtering backends that allow you to easily construct complex filters that can be specified by the client using query parameters. + +REST framework supports pluggable backends to implement filtering, and provides an implementation which uses the [django-filter] package. + +To use REST framework's filtering backend, first install `django-filter`. + + pip install django-filter + +You must also set the filter backend to `DjangoFilterBackend` in your settings: + + REST_FRAMEWORK = { + 'FILTER_BACKEND': 'rest_framework.filters.DjangoFilterBackend' + } + + +## Specifying filter fields + +If all you need is simple equality-based filtering, you can set a `filter_fields` attribute on the view, listing the set of fields you wish to filter against. + + class ProductList(generics.ListAPIView): + model = Product + serializer_class = ProductSerializer + filter_fields = ('category', 'in_stock') + +This will automatically create a `FilterSet` class for the given fields, and will allow you to make requests such as: + + http://example.com/api/products?category=clothing&in_stock=True + +## Specifying a FilterSet + +For more advanced filtering requirements you can specify a `FilterSet` class that should be used by the view. For example: + + class ProductFilter(django_filters.FilterSet): + min_price = django_filters.NumberFilter(lookup_type='gte') + max_price = django_filters.NumberFilter(lookup_type='lte') + class Meta: + model = Product + fields = ['category', 'in_stock', 'min_price', 'max_price'] + + class ProductList(generics.ListAPIView): + model = Product + serializer_class = ProductSerializer + filter_class = ProductFilter + +Which will allow you to make requests such as: + + http://example.com/api/products?category=clothing&max_price=10.00 + +For more details on using filter sets see the [django-filter documentation][django-filter-docs]. + +--- + +**Hints & Tips** + +* By default filtering is not enabled. If you want to use `DjangoFilterBackend` remember to make sure it is installed by using the `'FILTER_BACKEND'` setting. +* When using boolean fields, you should use the values `True` and `False` in the URL query parameters, rather than `0`, `1`, `true` or `false`. (The allowed boolean values are currently hardwired in Django's [NullBooleanSelect implementation][nullbooleanselect].) +* `django-filter` supports filtering across relationships, using Django's double-underscore syntax. + +--- + +## Overriding the initial queryset + +Note that you can use both an overridden `.get_queryset()` and generic filtering together, and everything will work as expected. For example, if `Product` had a many-to-many relationship with `User`, named `purchase`, you might want to write a view like this: + + class PurchasedProductsList(generics.ListAPIView): + """ + Return a list of all the products that the authenticated + user has ever purchased, with optional filtering. + """ + model = Product + serializer_class = ProductSerializer + filter_class = ProductFilter + + def get_queryset(self): + user = self.request.user + return user.purchase_set.all() +--- + +# Custom generic filtering + +You can also provide your own generic filtering backend, or write an installable app for other developers to use. + +To do so override `BaseFilterBackend`, and override the `.filter_queryset(self, request, queryset, view)` method. The method should return a new, filtered queryset. + +To install the filter backend, set the `'FILTER_BACKEND'` key in your `'REST_FRAMEWORK'` setting, using the dotted import path of the filter backend class. + +For example: + + REST_FRAMEWORK = { + 'FILTER_BACKEND': 'custom_filters.CustomFilterBackend' + } + +[cite]: https://docs.djangoproject.com/en/dev/topics/db/queries/#retrieving-specific-objects-with-filters +[django-filter]: https://github.com/alex/django-filter +[django-filter-docs]: https://django-filter.readthedocs.org/en/latest/index.html +[nullbooleanselect]: https://github.com/django/django/blob/master/django/forms/widgets.py
\ No newline at end of file diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 97b4441f..693e210d 100644 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -7,11 +7,11 @@ > > — [Django Documentation][cite] -One of the key benefits of class based views is the way they allow you to compose bits of reusable behaviour. REST framework takes advantage of this by providing a number of pre-built views that provide for commonly used patterns. +One of the key benefits of class based views is the way they allow you to compose bits of reusable behaviour. REST framework takes advantage of this by providing a number of pre-built views that provide for commonly used patterns. The generic views provided by REST framework allow you to quickly build API views that map closely to your database models. -If the generic views don't suit the needs of your API, you can drop down to using the regular `APIView` class, or reuse the mixins and base classes used by the generic views to compose your own set of reusable generic views. +If the generic views don't suit the needs of your API, you can drop down to using the regular `APIView` class, or reuse the mixins and base classes used by the generic views to compose your own set of reusable generic views. ## Examples @@ -29,8 +29,8 @@ For more complex cases you might also want to override various methods on the vi model = User serializer_class = UserSerializer permission_classes = (IsAdminUser,) - - def get_paginate_by(self): + + def get_paginate_by(self, queryset): """ Use smaller pagination for HTML representations. """ @@ -85,7 +85,7 @@ Extends: [SingleObjectAPIView], [DestroyModelMixin] Used for **update-only** endpoints for a **single model instance**. -Provides a `put` method handler. +Provides `put` and `patch` method handlers. Extends: [SingleObjectAPIView], [UpdateModelMixin] @@ -97,6 +97,14 @@ Provides `get` and `post` method handlers. Extends: [MultipleObjectAPIView], [ListModelMixin], [CreateModelMixin] +## RetrieveUpdateAPIView + +Used for **read or update** endpoints to represent a **single model instance**. + +Provides `get`, `put` and `patch` method handlers. + +Extends: [SingleObjectAPIView], [RetrieveModelMixin], [UpdateModelMixin] + ## RetrieveDestroyAPIView Used for **read or delete** endpoints to represent a **single model instance**. @@ -109,7 +117,7 @@ Extends: [SingleObjectAPIView], [RetrieveModelMixin], [DestroyModelMixin] Used for **read-write-delete** endpoints to represent a **single model instance**. -Provides `get`, `put` and `delete` method handlers. +Provides `get`, `put`, `patch` and `delete` method handlers. Extends: [SingleObjectAPIView], [RetrieveModelMixin], [UpdateModelMixin], [DestroyModelMixin] @@ -123,52 +131,90 @@ Each of the generic views provided is built by combining one of the base views b Extends REST framework's `APIView` class, adding support for serialization of model instances and model querysets. +**Attributes**: + +* `model` - The model that should be used for this view. Used as a fallback for determining the serializer if `serializer_class` is not set, and as a fallback for determining the queryset if `queryset` is not set. Otherwise not required. +* `serializer_class` - The serializer class that should be used for validating and deserializing input, and for serializing output. If unset, this defaults to creating a serializer class using `self.model`, with the `DEFAULT_MODEL_SERIALIZER_CLASS` setting as the base serializer class. + ## MultipleObjectAPIView Provides a base view for acting on a single object, by combining REST framework's `APIView`, and Django's [MultipleObjectMixin]. **See also:** ccbv.co.uk documentation for [MultipleObjectMixin][multiple-object-mixin-classy]. +**Attributes**: + +* `queryset` - The queryset that should be used for returning objects from this view. If unset, defaults to the default queryset manager for `self.model`. +* `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 overide 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`. + ## SingleObjectAPIView Provides a base view for acting on a single object, by combining REST framework's `APIView`, and Django's [SingleObjectMixin]. **See also:** ccbv.co.uk documentation for [SingleObjectMixin][single-object-mixin-classy]. +**Attributes**: + +* `queryset` - The queryset that should be used when retrieving an object from this view. If unset, defaults to the default queryset manager for `self.model`. +* `pk_kwarg` - The URL kwarg that should be used to look up objects by primary key. Defaults to `'pk'`. [Can only be set to non-default on Django 1.4+] +* `slug_url_kwarg` - The URL kwarg that should be used to look up objects by a slug. Defaults to `'slug'`. [Can only be set to non-default on Django 1.4+] +* `slug_field` - The field on the model that should be used to look up objects by a slug. If used, this should typically be set to a field with `unique=True`. Defaults to `'slug'`. + --- # Mixins -The mixin classes provide the actions that are used to provide the basic view behaviour. 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 behaviour. +The mixin classes provide the actions that are used to provide the basic view behaviour. 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 behaviour. ## 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. + +If the queryset is empty this returns a `200 OK` reponse, unless the `.allow_empty` attribute on the view is set to `False`, in which case it will return a `404 Not Found`. + Should be mixed in with [MultipleObjectAPIView]. ## CreateModelMixin Provides a `.create(request, *args, **kwargs)` method, that implements creating and saving a new model instance. +If an object is created this returns a `201 Created` response, with a serialized representation of the object as the body of the response. If the representation contains a key named `url`, then the `Location` header of the response will be populated with that value. + +If the request data provided for creating the object was invalid, a `400 Bad Request` response will be returned, with the error details as the body of the response. + Should be mixed in with any [GenericAPIView]. ## RetrieveModelMixin Provides a `.retrieve(request, *args, **kwargs)` method, that implements returning an existing model instance in a response. +If an object can be retrieve this returns a `200 OK` response, with a serialized representation of the object as the body of the response. Otherwise it will return a `404 Not Found`. + Should be mixed in with [SingleObjectAPIView]. ## UpdateModelMixin Provides a `.update(request, *args, **kwargs)` method, that implements updating and saving an existing model instance. +If an object is updated this returns a `200 OK` response, with a serialized representation of the object as the body of the response. + +If an object is created, for example when making a `DELETE` request followed by a `PUT` request to the same URL, this returns a `201 Created` response, with a serialized representation of the object as the body of the response. + +If the request data provided for updating the object was invalid, a `400 Bad Request` response will be returned, with the error details as the body of the response. + +A boolean `partial` keyword argument may be supplied to the `.update()` method. If `partial` is set to `True`, all fields for the update will be optional. This allows support for HTTP `PATCH` requests. + Should be mixed in with [SingleObjectAPIView]. ## DestroyModelMixin Provides a `.destroy(request, *args, **kwargs)` method, that implements deletion of an existing model instance. +If an object is deleted this returns a `204 No Content` response, otherwise it will return a `404 Not Found`. + Should be mixed in with [SingleObjectAPIView]. [cite]: https://docs.djangoproject.com/en/dev/ref/class-based-views/#base-vs-generic-views @@ -184,4 +230,4 @@ Should be mixed in with [SingleObjectAPIView]. [CreateModelMixin]: #createmodelmixin [RetrieveModelMixin]: #retrievemodelmixin [UpdateModelMixin]: #updatemodelmixin -[DestroyModelMixin]: #destroymodelmixin
\ No newline at end of file +[DestroyModelMixin]: #destroymodelmixin diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index 597baba4..71253afb 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -70,33 +70,34 @@ We could now use our pagination serializer in a view like this. # If page is not an integer, deliver first page. users = paginator.page(1) except EmptyPage: - # If page is out of range (e.g. 9999), deliver last page of results. + # If page is out of range (e.g. 9999), + # deliver last page of results. users = paginator.page(paginator.num_pages) serializer_context = {'request': request} - serializer = PaginatedUserSerializer(instance=users, + serializer = PaginatedUserSerializer(users, context=serializer_context) return Response(serializer.data) ## Pagination in the generic views -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, or by turning pagination off completely. +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 `PAGINATION_SERIALIZER` and `PAGINATE_BY` settings. For example. +The default pagination style may be set globally, using the `DEFAULT_PAGINATION_SERIALIZER_CLASS`, `PAGINATE_BY` and `PAGINATE_BY_PARAM` settings. For example. REST_FRAMEWORK = { - 'PAGINATION_SERIALIZER': ( - 'example_app.pagination.CustomPaginationSerializer', - ), - 'PAGINATE_BY': 10 + 'PAGINATE_BY': 10, + 'PAGINATE_BY_PARAM': 'page_size' } You can also set the pagination style on a per-view basis, using the `ListAPIView` generic class-based view. class PaginatedListView(ListAPIView): model = ExampleModel - pagination_serializer_class = CustomPaginationSerializer paginate_by = 10 + paginate_by_param = 'page_size' + +Note that using a `paginate_by` value of `None` will turn off pagination for the view. For more complex requirements such as serialization that differs depending on the requested media type you can override the `.get_paginate_by()` and `.get_pagination_serializer_class()` methods. @@ -122,4 +123,20 @@ For example, to nest a pair of links labelled 'prev' and 'next', and set the nam results_field = 'objects' +## Using your custom pagination serializer + +To have your custom pagination serializer be used by default, use the `DEFAULT_PAGINATION_SERIALIZER_CLASS` setting: + + REST_FRAMEWORK = { + 'DEFAULT_PAGINATION_SERIALIZER_CLASS': + 'example_app.pagination.CustomPaginationSerializer', + } + +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): + model = ExampleModel + pagination_serializer_class = CustomPaginationSerializer + paginate_by = 10 + [cite]: https://docs.djangoproject.com/en/dev/topics/pagination/ diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md index ac904720..de968557 100644 --- a/docs/api-guide/parsers.md +++ b/docs/api-guide/parsers.md @@ -37,7 +37,7 @@ You can also set the renderers used for an individual view, using the `APIView` Or, if you're using the `@api_view` decorator with function based views. - @api_view(('POST',)), + @api_view(['POST']) @parser_classes((YAMLParser,)) def example_view(request, format=None): """ @@ -140,6 +140,7 @@ For example: """ A naive raw file upload parser. """ + media_type = '*/*' # Accept anything def parse(self, stream, media_type=None, parser_context=None): content = stream.read() @@ -158,4 +159,17 @@ For example: files = {name: uploaded} return DataAndFiles(data, files) +--- + +# Third party packages + +The following third party packages are also available. + +## MessagePack + +[MessagePack][messagepack] is a fast, efficient binary serialization format. [Juan Riaza][juanriaza] maintains the [djangorestframework-msgpack][djangorestframework-msgpack] package which provides MessagePack renderer and parser support for REST framework. + [cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion +[messagepack]: https://github.com/juanriaza/django-rest-framework-msgpack +[juanriaza]: https://github.com/juanriaza +[djangorestframework-msgpack]: https://github.com/juanriaza/django-rest-framework-msgpack
\ No newline at end of file diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 0b7b32e9..fce68f6d 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -33,6 +33,12 @@ The default permission policy may be set globally, using the `DEFAULT_PERMISSION ) } +If not specified, this setting defaults to allowing unrestricted access: + + 'DEFAULT_PERMISSION_CLASSES': ( + 'rest_framework.permissions.AllowAny', + ) + You can also set the authentication policy on a per-view basis, using the `APIView` class based views. class ExampleView(APIView): @@ -47,7 +53,7 @@ You can also set the authentication policy on a per-view basis, using the `APIVi Or, if you're using the `@api_view` decorator with function based views. @api_view('GET') - @permission_classes(IsAuthenticated) + @permission_classes((IsAuthenticated, )) def example_view(request, format=None): content = { 'status': 'request was permitted' @@ -58,6 +64,12 @@ Or, if you're using the `@api_view` decorator with function based views. # API Reference +## AllowAny + +The `AllowAny` permission class will allow unrestricted access, **regardless of if the request was authenticated or unauthenticated**. + +This permission is not strictly required, since you can achieve the same result by using an empty list or tuple for the permissions setting, but you may find it useful to specify this class because it makes the intention explicit. + ## IsAuthenticated The `IsAuthenticated` permission class will deny permission to any unauthenticated user, and allow permission otherwise. @@ -66,7 +78,7 @@ This permission is suitable if you want your API to only be accessible to regist ## IsAdminUser -The `IsAdminUser` permission class will deny permission to any user, unless `user.is_staff`is `True` in which case permission will be allowed. +The `IsAdminUser` permission class will deny permission to any user, unless `user.is_staff` is `True` in which case permission will be allowed. This permission is suitable is you want your API to only be accessible to a subset of trusted administrators. diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md new file mode 100644 index 00000000..351b5e09 --- /dev/null +++ b/docs/api-guide/relations.md @@ -0,0 +1,139 @@ +<a class="github" href="relations.py"></a> + +# Serializer relations + +> Bad programmers worry about the code. +> Good programmers worry about data structures and their relationships. +> +> — [Linus Torvalds][cite] + + +Relational fields are used to represent model relationships. They can be applied to `ForeignKey`, `ManyToManyField` and `OneToOneField` relationships, as well as to reverse relationships, and custom relationships such as `GenericForeignKey`. + +--- + +**Note:** The relational fields are declared in `relations.py`, but by convention you should import them using `from rest_framework import serializers` and refer to fields as `serializers.<FieldName>`. + +--- + +## RelatedField + +This field can be applied to any of the following: + +* A `ForeignKey` field. +* A `OneToOneField` field. +* A reverse OneToOne relationship +* Any other "to-one" relationship. + +By default `RelatedField` will represent the target of the field using it's `__unicode__` method. + +You can customize this behavior by subclassing `ManyRelatedField`, and overriding the `.to_native(self, value)` method. + +## ManyRelatedField + +This field can be applied to any of the following: + +* A `ManyToManyField` field. +* A reverse ManyToMany relationship. +* A reverse ForeignKey relationship +* Any other "to-many" relationship. + +By default `ManyRelatedField` will represent the targets of the field using their `__unicode__` method. + +For example, given the following models: + + class TaggedItem(models.Model): + """ + Tags arbitrary model instances using a generic relation. + + See: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/ + """ + tag = models.SlugField() + content_type = models.ForeignKey(ContentType) + object_id = models.PositiveIntegerField() + content_object = GenericForeignKey('content_type', 'object_id') + + def __unicode__(self): + return self.tag + + + class Bookmark(models.Model): + """ + A bookmark consists of a URL, and 0 or more descriptive tags. + """ + url = models.URLField() + tags = GenericRelation(TaggedItem) + +And a model serializer defined like this: + + class BookmarkSerializer(serializers.ModelSerializer): + tags = serializers.ManyRelatedField(source='tags') + + class Meta: + model = Bookmark + exclude = ('id',) + +Then an example output format for a Bookmark instance would be: + + { + 'tags': [u'django', u'python'], + 'url': u'https://www.djangoproject.com/' + } + +## PrimaryKeyRelatedField +## ManyPrimaryKeyRelatedField + +`PrimaryKeyRelatedField` and `ManyPrimaryKeyRelatedField` will represent the target of the relationship using it's primary key. + +By default these fields are read-write, although you can change this behavior using the `read_only` flag. + +**Arguments**: + +* `queryset` - By default `ModelSerializer` classes will use the default queryset for the relationship. `Serializer` classes must either set a queryset explicitly, or set `read_only=True`. +* `null` - If set to `True`, the field will accept values of `None` or the empty-string for nullable relationships. + +## SlugRelatedField +## ManySlugRelatedField + +`SlugRelatedField` and `ManySlugRelatedField` will represent the target of the relationship using a unique slug. + +By default these fields read-write, although you can change this behavior using the `read_only` flag. + +**Arguments**: + +* `slug_field` - The field on the target that should be used to represent it. This should be a field that uniquely identifies any given instance. For example, `username`. +* `queryset` - By default `ModelSerializer` classes will use the default queryset for the relationship. `Serializer` classes must either set a queryset explicitly, or set `read_only=True`. +* `null` - If set to `True`, the field will accept values of `None` or the empty-string for nullable relationships. + +## HyperlinkedRelatedField +## ManyHyperlinkedRelatedField + +`HyperlinkedRelatedField` and `ManyHyperlinkedRelatedField` will represent the target of the relationship using a hyperlink. + +By default, `HyperlinkedRelatedField` is read-write, although you can change this behavior using the `read_only` flag. + +**Arguments**: + +* `view_name` - The view name that should be used as the target of the relationship. **required**. +* `format` - If using format suffixes, hyperlinked fields will use the same format suffix for the target unless overridden by using the `format` argument. +* `queryset` - By default `ModelSerializer` classes will use the default queryset for the relationship. `Serializer` classes must either set a queryset explicitly, or set `read_only=True`. +* `slug_field` - The field on the target that should be used for the lookup. Default is `'slug'`. +* `pk_url_kwarg` - The named url parameter for the pk field lookup. Default is `pk`. +* `slug_url_kwarg` - The named url parameter for the slug field lookup. Default is to use the same value as given for `slug_field`. +* `null` - If set to `True`, the field will accept values of `None` or the empty-string for nullable relationships. + +## HyperLinkedIdentityField + +This field can be applied as an identity relationship, such as the `'url'` field on a HyperlinkedModelSerializer. + +This field is always read-only. + +**Arguments**: + +* `view_name` - The view name that should be used as the target of the relationship. **required**. +* `format` - If using format suffixes, hyperlinked fields will use the same format suffix for the target unless overridden by using the `format` argument. +* `slug_field` - The field on the target that should be used for the lookup. Default is `'slug'`. +* `pk_url_kwarg` - The named url parameter for the pk field lookup. Default is `pk`. +* `slug_url_kwarg` - The named url parameter for the slug field lookup. Default is to use the same value as given for `slug_field`. + +[cite]: http://lwn.net/Articles/193245/ diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index b6db376c..b4f7ec3d 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -42,7 +42,7 @@ You can also set the renderers used for an individual view, using the `APIView` Or, if you're using the `@api_view` decorator with function based views. - @api_view(('GET',)), + @api_view(['GET']) @renderer_classes((JSONRenderer, JSONPRenderer)) def user_count_view(request, format=None): """ @@ -106,12 +106,12 @@ If you are considering using `XML` for your API, you may want to consider implem **.format**: `'.xml'` -## HTMLRenderer +## TemplateHTMLRenderer Renders data to HTML, using Django's standard template rendering. Unlike other renderers, the data passed to the `Response` does not need to be serialized. Also, unlike other renderers, you may want to include a `template_name` argument when creating the `Response`. -The HTMLRenderer will create a `RequestContext`, using the `response.data` as the context dict, and determine a template name to use to render the context. +The TemplateHTMLRenderer will create a `RequestContext`, using the `response.data` as the context dict, and determine a template name to use to render the context. The template name is determined by (in order of preference): @@ -119,27 +119,49 @@ The template name is determined by (in order of preference): 2. An explicit `.template_name` attribute set on this class. 3. The return result of calling `view.get_template_names()`. -An example of a view that uses `HTMLRenderer`: +An example of a view that uses `TemplateHTMLRenderer`: class UserInstance(generics.RetrieveUserAPIView): """ A view that returns a templated HTML representations of a given user. """ model = Users - renderer_classes = (HTMLRenderer,) + renderer_classes = (TemplateHTMLRenderer,) def get(self, request, *args, **kwargs) self.object = self.get_object() - return Response(self.object, template_name='user_detail.html') + return Response({'user': self.object}, template_name='user_detail.html') -You can use `HTMLRenderer` either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint. +You can use `TemplateHTMLRenderer` either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint. -If you're building websites that use `HTMLRenderer` along with other renderer classes, you should consider listing `HTMLRenderer` as the first class in the `renderer_classes` list, so that it will be prioritised first even for browsers that send poorly formed `ACCEPT:` headers. +If you're building websites that use `TemplateHTMLRenderer` along with other renderer classes, you should consider listing `TemplateHTMLRenderer` as the first class in the `renderer_classes` list, so that it will be prioritised first even for browsers that send poorly formed `ACCEPT:` headers. **.media_type**: `text/html` **.format**: `'.html'` +See also: `StaticHTMLRenderer` + +## StaticHTMLRenderer + +A simple renderer that simply returns pre-rendered HTML. Unlike other renderers, the data passed to the response object should be a string representing the content to be returned. + +An example of a view that uses `TemplateHTMLRenderer`: + + @api_view(('GET',)) + @renderer_classes((StaticHTMLRenderer,)) + def simple_html_view(request): + data = '<html><body><h1>Hello, world</h1></body></html>' + return Response(data) + +You can use `TemplateHTMLRenderer` either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint. + +**.media_type**: `text/html` + +**.format**: `'.html'` + +See also: `TemplateHTMLRenderer` + ## BrowsableAPIRenderer Renders data into HTML for the Browseable 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. @@ -207,7 +229,7 @@ In some cases you might want your view to use different serialization styles dep For example: @api_view(('GET',)) - @renderer_classes((HTMLRenderer, JSONRenderer)) + @renderer_classes((TemplateHTMLRenderer, JSONRenderer)) def list_users(request): """ A view that can return JSON or HTML representations @@ -215,9 +237,9 @@ For example: """ queryset = Users.objects.filter(active=True) - if request.accepted_media_type == 'text/html': + if request.accepted_renderer.format == 'html': # TemplateHTMLRenderer takes a context dict, - # and additionally requiresa 'template_name'. + # and additionally requires a 'template_name'. # It does not require serialization. data = {'users': queryset} return Response(data, template_name='list_users.html') @@ -235,6 +257,34 @@ In [the words of Roy Fielding][quote], "A REST API should spend almost all of it For good examples of custom media types, see GitHub's use of a custom [application/vnd.github+json] media type, and Mike Amundsen's IANA approved [application/vnd.collection+json] JSON-based hypermedia. +## HTML error views + +Typically a renderer will behave the same regardless of if it's dealing with a regular response, or with a response caused by an exception being raised, such as an `Http404` or `PermissionDenied` exception, or a subclass of `APIException`. + +If you're using either the `TemplateHTMLRenderer` or the `StaticHTMLRenderer` and an exception is raised, the behavior is slightly different, and mirrors [Django's default handling of error views][django-error-views]. + +Exceptions raised and handled by an HTML renderer will attempt to render using one of the following methods, by order of precedence. + +* Load and render a template named `{status_code}.html`. +* Load and render a template named `api_exception.html`. +* Render the HTTP status code and text, for example "404 Not Found". + +Templates will render with a `RequestContext` which includes the `status_code` and `details` keys. + +--- + +# Third party packages + +The following third party packages are also available. + +## MessagePack + +[MessagePack][messagepack] is a fast, efficient binary serialization format. [Juan Riaza][juanriaza] maintains the [djangorestframework-msgpack][djangorestframework-msgpack] package which provides MessagePack renderer and parser support for REST framework. + +## CSV + +Comma-separated values are a plain-text tabular data format, that can be easily imported into spreadsheet applications. [Mjumbe Poe][mjumbewu] maintains the [djangorestframework-csv][djangorestframework-csv] package which provides CSV renderer support for REST framework. + [cite]: https://docs.djangoproject.com/en/dev/ref/template-response/#the-rendering-process [conneg]: content-negotiation.md [browser-accept-headers]: http://www.gethifi.com/blog/browser-rest-http-accept-headers @@ -243,3 +293,9 @@ For good examples of custom media types, see GitHub's use of a custom [applicati [quote]: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven [application/vnd.github+json]: http://developer.github.com/v3/media/ [application/vnd.collection+json]: http://www.amundsen.com/media-types/collection/ +[django-error-views]: https://docs.djangoproject.com/en/dev/topics/http/views/#customizing-error-views +[messagepack]: http://msgpack.org/ +[juanriaza]: https://github.com/juanriaza +[mjumbewu]: https://github.com/mjumbewu +[djangorestframework-msgpack]: https://github.com/juanriaza/django-rest-framework-msgpack +[djangorestframework-csv]: https://github.com/mjumbewu/django-rest-framework-csv
\ No newline at end of file diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 057827d3..d98a602f 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -4,8 +4,7 @@ > Expanding the usefulness of the serializers is something that we would like to address. However, it's not a trivial problem, and it -will take some serious design work. Any offers to help out in this -area would be gratefully accepted. +will take some serious design work. > > — Russell Keith-Magee, [Django users group][cite] @@ -34,7 +33,7 @@ Declaring a serializer looks very similar to declaring a form: created = serializers.DateTimeField() def restore_object(self, attrs, instance=None): - if instance: + if instance is not None: instance.title = attrs['title'] instance.content = attrs['content'] instance.created = attrs['created'] @@ -47,7 +46,7 @@ The first part of serializer class defines the fields that get serialized/deseri We can now use `CommentSerializer` to serialize a comment, or list of comments. Again, using the `Serializer` class looks a lot like using a `Form` class. - serializer = CommentSerializer(instance=comment) + serializer = CommentSerializer(comment) serializer.data # {'email': u'leila@example.com', 'content': u'foo bar', 'created': datetime.datetime(2012, 8, 22, 16, 20, 9, 822774)} @@ -65,20 +64,33 @@ Deserialization is similar. First we parse a stream into python native datatype ...then we restore those native datatypes into a fully populated object instance. - serializer = CommentSerializer(data) + serializer = CommentSerializer(data=data) serializer.is_valid() # True serializer.object # <Comment object at 0x10633b2d0> >>> serializer.deserialize('json', stream) +When deserializing data, we can either create a new instance, or update an existing instance. + + serializer = CommentSerializer(data=data) # Create new instance + serializer = CommentSerializer(comment, data=data) # Update `instance` + +By default, serializers must be passed values for all required fields or they will throw validation errors. You can use the `partial` argument in order to allow partial updates. + + serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True) # Update `instance` with partial data + ## Validation When deserializing data, you always need to call `is_valid()` before attempting to access the deserialized object. If any validation errors occur, the `.errors` and `.non_field_errors` properties will contain the resulting error messages. ### Field-level validation -You can specify custom field-level validation by adding `validate_<fieldname>()` methods to your `Serializer` subclass. These are analagous to `clean_<fieldname>` methods on Django forms, but accept slightly different arguments. They take a dictionary of deserialized attributes as a first argument, and the field name in that dictionary as a second argument (which will be either the name of the field or the value of the `source` argument to the field, if one was provided). Your `validate_<fieldname>` methods should either just return the attrs dictionary or raise a `ValidationError`. For example: +You can specify custom field-level validation by adding `.validate_<fieldname>` methods to your `Serializer` subclass. These are analagous to `.clean_<fieldname>` methods on Django forms, but accept slightly different arguments. + +They take a dictionary of deserialized attributes as a first argument, and the field name in that dictionary as a second argument (which will be either the name of the field or the value of the `source` argument to the field, if one was provided). + +Your `validate_<fieldname>` methods should either just return the `attrs` dictionary or raise a `ValidationError`. For example: from rest_framework import serializers @@ -88,16 +100,37 @@ You can specify custom field-level validation by adding `validate_<fieldname>()` def validate_title(self, attrs, source): """ - Check that the blog post is about Django + Check that the blog post is about Django. """ value = attrs[source] - if "Django" not in value: + if "django" not in value.lower(): raise serializers.ValidationError("Blog post is not about Django") return attrs -### Final cross-field validation +### Object-level validation + +To do any other validation that requires access to multiple fields, add a method called `.validate()` to your `Serializer` subclass. This method takes a single argument, which is the `attrs` dictionary. It should raise a `ValidationError` if necessary, or just return `attrs`. For example: + + from rest_framework import serializers + + class EventSerializer(serializers.Serializer): + description = serializers.CahrField(max_length=100) + start = serializers.DateTimeField() + finish = serializers.DateTimeField() + + def validate(self, attrs): + """ + Check that the start is before the stop. + """ + if attrs['start'] < attrs['finish']: + raise serializers.ValidationError("finish must occur after start") + return attrs + +## Saving object state -To do any other validation that requires access to multiple fields, add a method called `validate` to your `Serializer` subclass. This method takes a single argument, which is the `attrs` dictionary. It should raise a `ValidationError` if necessary, or just return `attrs`. +Serializers also include a `.save()` method that you can override if you want to provide a method of persisting the state of a deserialized object. The default behavior of the method is to simply call `.save()` on the deserialized object instance. + +The generic views provided by REST framework call the `.save()` method when updating or creating entities. ## Dealing with nested objects @@ -107,21 +140,21 @@ where some of the attributes of an object might not be simple datatypes such as The `Serializer` class is itself a type of `Field`, and can be used to represent relationships where one object type is nested inside another. class UserSerializer(serializers.Serializer): - email = serializers.EmailField() - username = serializers.CharField() - - def restore_object(self, attrs, instance=None): - return User(**attrs) - + email = serializers.Field() + username = serializers.Field() class CommentSerializer(serializers.Serializer): user = UserSerializer() - title = serializers.CharField() - content = serializers.CharField(max_length=200) - created = serializers.DateTimeField() - - def restore_object(self, attrs, instance=None): - return Comment(**attrs) + title = serializers.Field() + content = serializers.Field() + created = serializers.Field() + +--- + +**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. + +--- + ## Creating custom fields @@ -135,7 +168,6 @@ Let's look at an example of serializing a class that represents an RGB color val """ A color represented in the RGB colorspace. """ - def __init__(self, red, green, blue): assert(red >= 0 and green >= 0 and blue >= 0) assert(red < 256 and green < 256 and blue < 256) @@ -145,7 +177,6 @@ Let's look at an example of serializing a class that represents an RGB color val """ Color objects are serialized into "rgb(#, #, #)" notation. """ - def to_native(self, obj): return "rgb(%d, %d, %d)" % (obj.red, obj.green, obj.blue) @@ -177,7 +208,7 @@ As an example, let's create a field that can be used represent the class name of # ModelSerializers Often you'll want serializer classes that map closely to model definitions. -The `ModelSerializer` class lets you automatically create a Serializer class with fields that corrospond to the Model fields. +The `ModelSerializer` class lets you automatically create a Serializer class with fields that correspond to the Model fields. class AccountSerializer(serializers.ModelSerializer): class Meta: @@ -190,7 +221,7 @@ The `ModelSerializer` class lets you automatically create a Serializer class wit You can add extra fields to a `ModelSerializer` or override the default fields by declaring fields on the class, just as you would for a `Serializer` class. class AccountSerializer(serializers.ModelSerializer): - url = CharField(source='get_absolute_url', readonly=True) + url = CharField(source='get_absolute_url', read_only=True) group = NaturalKeyField() class Meta: @@ -225,40 +256,63 @@ For example: ## Specifiying nested serialization -The default `ModelSerializer` uses primary keys for relationships, but you can also easily generate nested representations using the `nested` option: +The default `ModelSerializer` uses primary keys for relationships, but you can also easily generate nested representations using the `depth` option: class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account exclude = ('id',) - nested = True - -The `nested` option may be set to either `True`, `False`, or an integer value. If given an integer value it indicates the depth of relationships that should be traversed before reverting to a flat representation. + depth = 1 -When serializing objects using a nested representation any occurances of recursion will be recognised, and will fall back to using a flat representation. - -## Customising the default fields used by a ModelSerializer +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. +## Specifying which fields should be read-only +You may wish to specify multiple fields as read-only. Instead of adding each field explicitely with the `read_only=True` attribute, you may use the `read_only_fields` Meta option, like so: class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account + read_only_fields = ('created', 'modified') - def get_pk_field(self, model_field): - return serializers.Field(readonly=True) +## Customising the default fields + +You can create customized subclasses of `ModelSerializer` that use a different set of default fields for the representation, by overriding various `get_<field_type>_field` methods. + +Each of these methods may either return a field or serializer instance, or `None`. + +### get_pk_field + +**Signature**: `.get_pk_field(self, model_field)` - def get_nested_field(self, model_field): - return serializers.ModelSerializer() +Returns the field instance that should be used to represent the pk field. - def get_related_field(self, model_field, to_many=False): - queryset = model_field.rel.to._default_manager - if to_many: - return serializers.ManyRelatedField(queryset=queryset) - return serializers.RelatedField(queryset=queryset) +### get_nested_field + +**Signature**: `.get_nested_field(self, model_field)` + +Returns the field instance that should be used to represent a related field when `depth` is specified as being non-zero. + +### get_related_field + +**Signature**: `.get_related_field(self, model_field, to_many=False)` + +Returns the field instance that should be used to represent a related field when `depth` is not specified, or when nested representations are being used and the depth reaches zero. + +### get_field + +**Signature**: `.get_field(self, model_field)` + +Returns the field instance that should be used for non-relational, non-pk fields. + +### Example: + +The following custom model serializer could be used as a base class for model serializers that should always exclude the pk by default. + + class NoPKModelSerializer(serializers.ModelSerializer): + def get_pk_field(self, model_field): + return None - def get_field(self, model_field): - return serializers.ModelField(model_field=model_field) [cite]: https://groups.google.com/d/topic/django-users/sVFaOfQi4wY/discussion diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 21efc853..a422e5f6 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -13,7 +13,7 @@ For example your project's `settings.py` file might include something like this: REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.YAMLRenderer', - ) + ), 'DEFAULT_PARSER_CLASSES': ( 'rest_framework.parsers.YAMLParser', ) @@ -42,7 +42,7 @@ Default: ( 'rest_framework.renderers.JSONRenderer', - 'rest_framework.renderers.BrowsableAPIRenderer' + 'rest_framework.renderers.BrowsableAPIRenderer', 'rest_framework.renderers.TemplateHTMLRenderer' ) @@ -65,14 +65,18 @@ Default: ( 'rest_framework.authentication.SessionAuthentication', - 'rest_framework.authentication.UserBasicAuthentication' + 'rest_framework.authentication.BasicAuthentication' ) ## DEFAULT_PERMISSION_CLASSES A list or tuple of permission classes, that determines the default set of permissions checked at the start of a view. -Default: `()` +Default: + + ( + 'rest_framework.permissions.AllowAny', + ) ## DEFAULT_THROTTLE_CLASSES @@ -92,11 +96,21 @@ Default: `rest_framework.serializers.ModelSerializer` Default: `rest_framework.pagination.PaginationSerializer` -## FORMAT_SUFFIX_KWARG +## FILTER_BACKEND -**TODO** +The filter backend class that should be used for generic filtering. If set to `None` then generic filtering is disabled. -Default: `'format'` +## PAGINATE_BY + +The default page size to use for pagination. If set to `None`, pagination is disabled by default. + +Default: `None` + +## PAGINATE_BY_PARAM + +The name of a query parameter, which can be used by the client to overide the default page size to use for pagination. If set to `None`, clients may not override the default page size. + +Default: `None` ## UNAUTHENTICATED_USER @@ -146,4 +160,10 @@ Default: `'accept'` Default: `'format'` +## FORMAT_SUFFIX_KWARG + +**TODO** + +Default: `'format'` + [cite]: http://www.python.org/dev/peps/pep-0020/ diff --git a/docs/api-guide/status-codes.md b/docs/api-guide/status-codes.md index 401f45ce..b50c96ae 100644 --- a/docs/api-guide/status-codes.md +++ b/docs/api-guide/status-codes.md @@ -87,7 +87,7 @@ Response status codes beginning with the digit "5" indicate cases in which the s HTTP_503_SERVICE_UNAVAILABLE HTTP_504_GATEWAY_TIMEOUT HTTP_505_HTTP_VERSION_NOT_SUPPORTED - HTTP_511_NETWORD_AUTHENTICATION_REQUIRED + HTTP_511_NETWORK_AUTHENTICATION_REQUIRED [rfc2324]: http://www.ietf.org/rfc/rfc2324.txt diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index d54433b1..b03bc9e0 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -31,9 +31,9 @@ The default throttling policy may be set globally, using the `DEFAULT_THROTTLE_C REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( - 'rest_framework.throttles.AnonThrottle', - 'rest_framework.throttles.UserThrottle', - ) + 'rest_framework.throttling.AnonRateThrottle', + 'rest_framework.throttling.UserRateThrottle' + ), 'DEFAULT_THROTTLE_RATES': { 'anon': '100/day', 'user': '1000/day' @@ -102,8 +102,8 @@ For example, multiple user throttle rates could be implemented by using the foll REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( 'example.throttles.BurstRateThrottle', - 'example.throttles.SustainedRateThrottle', - ) + 'example.throttles.SustainedRateThrottle' + ), 'DEFAULT_THROTTLE_RATES': { 'burst': '60/min', 'sustained': '1000/day' @@ -136,8 +136,8 @@ For example, given the following views... REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( - 'rest_framework.throttles.ScopedRateThrottle', - ) + 'rest_framework.throttling.ScopedRateThrottle' + ), 'DEFAULT_THROTTLE_RATES': { 'contacts': '1000/day', 'uploads': '20/day' diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index e3fbadb2..d1e42ec1 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -19,6 +19,10 @@ Using the `APIView` class is pretty much the same as using a regular `View` clas For example: + from rest_framework.views import APIView + from rest_framework.response import Response + from rest_framework import authentication, permissions + class ListUsers(APIView): """ View to list all users in the system. @@ -118,9 +122,51 @@ You won't typically need to override this method. > > — [Nick Coghlan][cite2] -REST framework also gives you to work with regular function based views... +REST framework also allows you to work with regular function based views. It provides a set of simple decorators that wrap your function based views to ensure they receive an instance of `Request` (rather than the usual Django `HttpRequest`) and allows them to return a `Response` (instead of a Django `HttpResponse`), and allow you to configure how the request is processed. + +## @api_view() + +**Signature:** `@api_view(http_method_names)` + +The core of this functionality is the `api_view` decorator, which takes a list of HTTP methods that your view should respond to. For example, this is how you would write a very simple view that just manually returns some data: + + from rest_framework.decorators import api_view + + @api_view(['GET']) + def hello_world(request): + return Response({"message": "Hello, world!"}) + + +This view will use the default renderers, parsers, authentication classes etc specified in the [settings](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: + + from rest_framework.decorators import api_view, throttle_classes + from rest_framework.throttling import UserRateThrottle + + class OncePerDayUserThrottle(UserRateThrottle): + rate = '1/day' + + @api_view(['GET']) + @throttle_classes([OncePerDayUserThrottle]) + def view(request): + return Response({"message": "Hello for today! See you tomorrow!"}) + +These decorators correspond to the attributes set on `APIView` subclasses, described above. + +The available decorators are: + +* `@renderer_classes(...)` +* `@parser_classes(...)` +* `@authentication_classes(...)` +* `@throttle_classes(...)` +* `@permission_classes(...)` -**[TODO]** +Each of these decorators takes a single argument which must be a list or tuple of classes. [cite]: http://reinout.vanrees.org/weblog/2011/08/24/class-based-views-usage.html [cite2]: http://www.boredomandlaziness.org/2012/05/djangos-cbvs-are-not-mistake-but.html +[settings]: api-guide/settings.md +[throttling]: api-guide/throttling.md |
