diff options
| author | Xavier Ordoquy | 2014-04-13 00:05:57 +0200 | 
|---|---|---|
| committer | Xavier Ordoquy | 2014-04-13 00:05:57 +0200 | 
| commit | d08536ad9d026fb7126c430f6d9c18f8540aacd6 (patch) | |
| tree | a8a1d36ce76867e57da23379694ea0609801990b /docs/api-guide | |
| parent | 2911cd64ad67ba193e3d37322ee71692cb482623 (diff) | |
| parent | 93b9245b8714287a440023451ff7880a2f6e5b32 (diff) | |
| download | django-rest-framework-d08536ad9d026fb7126c430f6d9c18f8540aacd6.tar.bz2 | |
Merge remote-tracking branch 'origin/master' into 2.4.0
Conflicts:
	.travis.yml
	docs/api-guide/fields.md
	docs/api-guide/routers.md
	docs/topics/release-notes.md
	rest_framework/authentication.py
	rest_framework/serializers.py
	rest_framework/templatetags/rest_framework.py
	rest_framework/tests/test_authentication.py
	rest_framework/tests/test_filters.py
	rest_framework/tests/test_hyperlinkedserializers.py
	rest_framework/tests/test_serializer.py
	rest_framework/tests/test_testing.py
	rest_framework/utils/encoders.py
	tox.ini
Diffstat (limited to 'docs/api-guide')
| -rwxr-xr-x | docs/api-guide/authentication.md | 34 | ||||
| -rw-r--r-- | docs/api-guide/exceptions.md | 6 | ||||
| -rw-r--r-- | docs/api-guide/fields.md | 44 | ||||
| -rw-r--r-- | docs/api-guide/filtering.md | 38 | ||||
| -rwxr-xr-x | docs/api-guide/generic-views.md | 13 | ||||
| -rw-r--r-- | docs/api-guide/pagination.md | 10 | ||||
| -rw-r--r-- | docs/api-guide/relations.md | 5 | ||||
| -rw-r--r-- | docs/api-guide/routers.md | 14 | ||||
| -rw-r--r-- | docs/api-guide/serializers.md | 50 | ||||
| -rw-r--r-- | docs/api-guide/settings.md | 18 | ||||
| -rw-r--r-- | docs/api-guide/testing.md | 4 | ||||
| -rw-r--r-- | docs/api-guide/throttling.md | 2 | ||||
| -rw-r--r-- | docs/api-guide/viewsets.md | 2 | 
13 files changed, 199 insertions, 41 deletions
| diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 53efc49a..88a7a011 100755 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -93,7 +93,7 @@ Note that if deploying to [Apache using mod_wsgi][mod_wsgi_official], the author  If you are deploying to Apache, and using any non-session based authentication, you will need to explicitly configure mod_wsgi to pass the required headers through to the application.  This can be done by specifying the `WSGIPassAuthorization` directive in the appropriate context and setting it to `'On'`. -    # this can go in either server config, virtual host, directory or .htaccess  +    # this can go in either server config, virtual host, directory or .htaccess      WSGIPassAuthorization On  --- @@ -117,7 +117,7 @@ Unauthenticated responses that are denied permission will result in an `HTTP 401  ## TokenAuthentication -This authentication scheme uses a simple token-based HTTP Authentication scheme.  Token authentication is appropriate for client-server setups, such as native desktop and mobile clients.  +This authentication scheme uses a simple token-based HTTP Authentication scheme.  Token authentication is appropriate for client-server setups, such as native desktop and mobile clients.  To use the `TokenAuthentication` scheme, include `rest_framework.authtoken` in your `INSTALLED_APPS` setting: @@ -125,7 +125,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. The `authtoken` database tables are managed by south (see [Schema migrations](#schema-migrations) below).  You'll also need to create tokens for your users. @@ -209,7 +209,7 @@ You can do so by inserting a `needed_by` attribute in your user migration:          needed_by = (              ('authtoken', '0001_initial'),          ) -         +          def forwards(self):              ... @@ -282,7 +282,7 @@ Note that the `namespace='oauth2'` argument is required.  Finally, sync your database.      python manage.py syncdb -    python manage.py migrate  +    python manage.py migrate  --- @@ -368,7 +368,7 @@ The following example will authenticate any incoming request as the user given b                  user = User.objects.get(username=username)              except User.DoesNotExist:                  raise exceptions.AuthenticationFailed('No such user') -             +              return (user, None)  --- @@ -389,6 +389,18 @@ The [Django OAuth Toolkit][django-oauth-toolkit] package provides OAuth 2.0 supp  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. +## JSON Web Token Authentication + +JSON Web Token is a fairly new standard which can be used for token-based authentication. Unlike the built-in TokenAuthentication scheme, JWT Authentication doesn't need to use a database to validate a token. [Blimp][blimp] maintains the [djangorestframework-jwt][djangorestframework-jwt] package which provides a JWT Authentication class as well as a mechanism for clients to obtain a JWT given the username and password. + +## Hawk HTTP Authentication + +The [HawkREST][hawkrest] library builds on the [Mohawk][mohawk] library to let you work with [Hawk][hawk] signed requests and responses in your API. [Hawk][hawk] lets two parties securely communicate with each other using messages signed by a shared key. It is based on [HTTP MAC access authentication][mac] (which was based on parts of [OAuth 1.0][oauth-1.0a]). + +## HTTP Signature Authentication + +HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a way to achieve origin authentication and message integrity for HTTP messages. Similar to [Amazon's HTTP Signature scheme][amazon-http-signature], used by many of its services, it permits stateless, per-request authentication. [Elvio Toccalino][etoccalino] maintains the [djangorestframework-httpsignature][djangorestframework-httpsignature] package which provides an easy to use HTTP Signature Authentication mechanism. +  [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 @@ -413,3 +425,13 @@ The [Django OAuth2 Consumer][doac] library from [Rediker Software][rediker] is a  [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# +[blimp]: https://github.com/GetBlimp +[djangorestframework-jwt]: https://github.com/GetBlimp/django-rest-framework-jwt +[etoccalino]: https://github.com/etoccalino/ +[djangorestframework-httpsignature]: https://github.com/etoccalino/django-rest-framework-httpsignature +[amazon-http-signature]: http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html +[http-signature-ietf-draft]: https://datatracker.ietf.org/doc/draft-cavage-http-signatures/ +[hawkrest]: http://hawkrest.readthedocs.org/en/latest/ +[hawk]: https://github.com/hueniverse/hawk +[mohawk]: http://mohawk.readthedocs.org/en/latest/ +[mac]: http://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token-05 diff --git a/docs/api-guide/exceptions.md b/docs/api-guide/exceptions.md index 221df679..66e18173 100644 --- a/docs/api-guide/exceptions.md +++ b/docs/api-guide/exceptions.md @@ -18,7 +18,7 @@ The handled exceptions are:  In each case, REST framework will return a response with an appropriate status code and content-type.  The body of the response will include any additional details regarding the nature of the error. -By default all error responses will include a key `details` in the body of the response, but other keys may also be included. +By default all error responses will include a key `detail` in the body of the response, but other keys may also be included.  For example, the following request: @@ -86,7 +86,7 @@ Note that the exception handler will only be called for responses generated by r  The **base class** for all exceptions raised inside REST framework. -To provide a custom exception, subclass `APIException` and set the `.status_code` and `.detail` properties on the class. +To provide a custom exception, subclass `APIException` and set the `.status_code` and `.default_detail` properties on the class.  For example, if your API relies on a third party service that may sometimes be unreachable, you might want to implement an exception for the "503 Service Unavailable" HTTP response code.  You could do this like so: @@ -94,7 +94,7 @@ For example, if your API relies on a third party service that may sometimes be u      class ServiceUnavailable(APIException):          status_code = 503 -        detail = 'Service temporarily unavailable, try again later.' +        default_detail = 'Service temporarily unavailable, try again later.'  ## ParseError diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 83825350..883734a3 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -2,7 +2,7 @@  # Serializer fields -> Each field in a Form class is responsible not only for validating data, but also for "cleaning" it — normalizing it to a consistent format.  +> Each field in a Form class is responsible not only for validating data, but also for "cleaning" it — normalizing it to a consistent format.  >  > — [Django documentation][cite] @@ -28,7 +28,13 @@ 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. +Set this to `True` to ensure that the field is used when serializing a representation, but is not used when creating or updating an instance during deserialization. + +Defaults to `False` + +### `write_only` + +Set this to `True` to ensure that the field may be used when updating or creating an instance, but is not included when serializing the representation.  Defaults to `False` @@ -41,7 +47,7 @@ Defaults to `True`.  ### `default` -If set, this gives the default value that will be used for the field if no input value is supplied.  If not set the default behavior is to not populate the attribute at all.  +If set, this gives the default value that will be used for the field if no input value is supplied.  If not set the default behavior is to not populate the attribute at all.  May be set to a function or other callable, in which case the value will be evaluated each time it is used. @@ -86,7 +92,7 @@ For example, using the following model.          name = models.CharField(max_length=100)          created = models.DateTimeField(auto_now_add=True)          payment_expiry = models.DateTimeField() -         +          def has_expired(self):              return now() > self.payment_expiry @@ -96,8 +102,9 @@ A serializer definition that looked like this:      class AccountSerializer(serializers.HyperlinkedModelSerializer):          expired = serializers.Field(source='has_expired') -         +          class Meta: +            model = Account              fields = ('url', 'owner', 'name', 'expired')  Would produce output similar to: @@ -105,7 +112,7 @@ Would produce output similar to:      {          'url': 'http://example.com/api/accounts/3/',          'owner': 'http://example.com/api/users/12/', -        'name': 'FooCorp business account',  +        'name': 'FooCorp business account',          'expired': True      } @@ -119,7 +126,7 @@ A field that supports both read and write operations.  By itself `WritableField`  ## ModelField -A generic field that can be tied to any arbitrary model field.  The `ModelField` class delegates the task of serialization/deserialization to it's associated model field.  This field can be used to create serializer fields for custom model fields, without having to create a new custom serializer field. +A generic field that can be tied to any arbitrary model field.  The `ModelField` class delegates the task of serialization/deserialization to its associated model field.  This field can be used to create serializer fields for custom model fields, without having to create a new custom serializer field.  The `ModelField` class is generally intended for internal use, but can be used by your API if needed.  In order to properly instantiate a `ModelField`, it must be passed a field that is attached to an instantiated model.  For example: `ModelField(model_field=MyModel()._meta.get_field('custom_field'))` @@ -168,13 +175,13 @@ or `django.db.models.fields.TextField`.  Corresponds to `django.db.models.fields.URLField`.  Uses Django's `django.core.validators.URLValidator` for validation. -**Signature:** `CharField(max_length=200, min_length=None, allow_none=False)` +**Signature:** `URLField(max_length=200, min_length=None)`  ## SlugField  Corresponds to `django.db.models.fields.SlugField`. -**Signature:** `CharField(max_length=50, min_length=None, allow_none=False)` +**Signature:** `SlugField(max_length=50, min_length=None)`  ## ChoiceField @@ -218,7 +225,7 @@ In the case of JSON this means the default datetime representation uses the [ECM  **Signature:** `DateTimeField(format=None, input_formats=None)` -* `format` - A string representing the output format.  If not specified, this defaults to `None`, which indicates that Python `datetime` objects should be returned by `to_native`.  In this case the datetime encoding will be determined by the renderer.  +* `format` - A string representing the output format.  If not specified, this defaults to `None`, which indicates that Python `datetime` objects should be returned by `to_native`.  In this case the datetime encoding will be determined by the renderer.  * `input_formats` - A list of strings representing the input formats which may be used to parse the date.  If not specified, the `DATETIME_INPUT_FORMATS` setting will be used, which defaults to `['iso-8601']`.  DateTime format strings may either be [Python strftime formats][strftime] which explicitly specify the format, or the special string `'iso-8601'`, which indicates that [ISO 8601][iso8601] style datetimes should be used. (eg `'2013-01-29T12:34:56.000000Z'`) @@ -278,7 +285,7 @@ Corresponds to `django.forms.fields.FileField`.  **Signature:** `FileField(max_length=None, allow_empty_file=False)`   - `max_length` designates the maximum length for the file name. -   +   - `allow_empty_file` designates if empty files are allowed.  ## ImageField @@ -302,7 +309,7 @@ Django's regular [FILE_UPLOAD_HANDLERS] are used for handling uploaded files.  If you want to create a custom field, you'll probably want to override either one or both of the `.to_native()` and `.from_native()` methods.  These two methods are used to convert between the initial datatype, and a primitive, serializable datatype.  Primitive datatypes may be any of a number, string, date/time/datetime or None.  They may also be any list or dictionary like object that only contains other primitive objects. -The `.to_native()` method is called to convert the initial datatype into a primitive, serializable datatype.  The `from_native()` method is called to restore a primitive datatype into it's initial representation. +The `.to_native()` method is called to convert the initial datatype into a primitive, serializable datatype.  The `from_native()` method is called to restore a primitive datatype into its initial representation.  ## Examples @@ -323,12 +330,12 @@ Let's look at an example of serializing a class that represents an RGB color val          """          def to_native(self, obj):              return "rgb(%d, %d, %d)" % (obj.red, obj.green, obj.blue) -       +          def from_native(self, data):              data = data.strip('rgb(').rstrip(')')              red, green, blue = [int(col) for col in data.split(',')]              return Color(red, green, blue) -             +  By default field values are treated as mapping to an attribute on the object.  If you need to customize how the field value is accessed and set you need to override `.field_to_native()` and/or `.field_from_native()`. @@ -341,8 +348,17 @@ As an example, let's create a field that can be used represent the class name of              """              return obj.__class__ +# Third party packages + +The following third party packages are also available. + +## DRF Compound Fields + +The [drf-compound-fields][drf-compound-fields] package provides "compound" serializer fields, such as lists of simple values, which can be described by other fields rather than serializers with the `many=True` option. Also provided are fields for typed dictionaries and values that can be either a specific type or a list of items of that type. +  [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  [ecma262]: http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15  [strftime]: http://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior  [iso8601]: http://www.w3.org/TR/NOTE-datetime +[drf-compound-fields]: http://drf-compound-fields.readthedocs.org diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index 0e02a2a7..6a8a267b 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -24,7 +24,7 @@ For example:      from myapp.serializers import PurchaseSerializer      from rest_framework import generics -    class PurchaseList(generics.ListAPIView) +    class PurchaseList(generics.ListAPIView):          serializer_class = PurchaseSerializer          def get_queryset(self): @@ -46,7 +46,7 @@ For example if your URL config contained an entry like this:  You could then write a view that returned a purchase queryset filtered by the username portion of the URL: -    class PurchaseList(generics.ListAPIView) +    class PurchaseList(generics.ListAPIView):          serializer_class = PurchaseSerializer          def get_queryset(self): @@ -63,7 +63,7 @@ A final example of filtering the initial queryset would be to determine the init  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) +    class PurchaseList(generics.ListAPIView):          serializer_class = PurchaseSerializer          def get_queryset(self): @@ -264,13 +264,17 @@ For example:      search_fields = ('=username', '=email') +By default, the search parameter is named `'search`', but this may be overridden with the `SEARCH_PARAM` setting. +  For more details, see the [Django documentation][search-django-admin].  ---  ## OrderingFilter -The `OrderingFilter` class supports simple query parameter controlled ordering of results.  To specify the result order, set a query parameter named `'ordering'` to the required field name.  For example: +The `OrderingFilter` class supports simple query parameter controlled ordering of results.  By default, the query parameter is named `'ordering'`, but this may by overridden with the `ORDERING_PARAM` setting. + +For example, to order users by username:      http://example.com/api/users?ordering=username @@ -282,13 +286,37 @@ Multiple orderings may also be specified:      http://example.com/api/users?ordering=account,username +### Specifying which fields may be ordered against + +It's recommended that you explicitly specify which fields the API should allowing in the ordering filter.  You can do this by setting an `ordering_fields` attribute on the view, like so: + +    class UserListView(generics.ListAPIView): +        queryset = User.objects.all() +        serializer_class = UserSerializer +        filter_backends = (filters.OrderingFilter,) +        ordering_fields = ('username', 'email') + +This helps prevent unexpected data leakage, such as allowing users to order against a password hash field or other sensitive data. + +If you *don't* specify an `ordering_fields` attribute on the view, the filter class will default to allowing the user to filter on any readable fields on the serializer specified by the `serializer_class` attribute. + +If you are confident that the queryset being used by the view doesn't contain any sensitive data, you can also explicitly specify that a view should allow ordering on *any* model field or queryset aggregate, by using the special value `'__all__'`. + +    class BookingsListView(generics.ListAPIView): +        queryset = Booking.objects.all() +        serializer_class = BookingSerializer +        filter_backends = (filters.OrderingFilter,) +        ordering_fields = '__all__' + +### Specifying a default ordering +  If an `ordering` attribute is set on the view, this will be used as the default ordering.  Typically you'd instead control this by setting `order_by` on the initial queryset, but using the `ordering` parameter on the view allows you to specify the ordering in a way that it can then be passed automatically as context to a rendered template.  This makes it possible to automatically render column headers differently if they are being used to order the results.      class UserListView(generics.ListAPIView):          queryset = User.objects.all() -        serializer = UserSerializer +        serializer_class = UserSerializer          filter_backends = (filters.OrderingFilter,)          ordering = ('username',)  diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 83c3e45f..fb927ea8 100755 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -119,7 +119,7 @@ For example:          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. +Note that if your API doesn't include any object level permissions, you may optionally exclude the `self.check_object_permissions`, and simply return the object from the `get_object_or_404` lookup.  #### `get_filter_backends(self)` @@ -362,11 +362,20 @@ If you are using a mixin across multiple views, you can take this a step further  Using custom base classes is a good option if you have custom behavior that consistently needs to be repeated across a large number of views throughout your project. -[cite]: https://docs.djangoproject.com/en/dev/ref/class-based-views/#base-vs-generic-views +# Third party packages + +The following third party packages provide additional generic view implementations. + +## Django REST Framework bulk +The [django-rest-framework-bulk package][django-rest-framework-bulk] implements generic view mixins as well as some common concrete generic views to allow to apply bulk operations via API requests. + + +[cite]: https://docs.djangoproject.com/en/dev/ref/class-based-views/#base-vs-generic-views  [GenericAPIView]: #genericapiview  [ListModelMixin]: #listmodelmixin  [CreateModelMixin]: #createmodelmixin  [RetrieveModelMixin]: #retrievemodelmixin  [UpdateModelMixin]: #updatemodelmixin  [DestroyModelMixin]: #destroymodelmixin +[django-rest-framework-bulk]: https://github.com/miki725/django-rest-framework-bulk diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index 0829589f..efc4ae7f 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -147,4 +147,14 @@ Alternatively, to set your custom pagination serializer on a per-view basis, use          pagination_serializer_class = CustomPaginationSerializer          paginate_by = 10 +# Third party packages + +The following third party packages are also available. + +## DRF-extensions + +The [`DRF-extensions` package][drf-extensions] includes a [`PaginateByMaxMixin` mixin class][paginate-by-max-mixin] that allows your API clients to specify `?page_size=max` to obtain the maximum allowed page size. +  [cite]: https://docs.djangoproject.com/en/dev/topics/pagination/ +[drf-extensions]: http://chibisov.github.io/drf-extensions/docs/ +[paginate-by-max-mixin]: http://chibisov.github.io/drf-extensions/docs/#paginatebymaxmixin
\ No newline at end of file diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md index 1b089c54..cc4f5585 100644 --- a/docs/api-guide/relations.md +++ b/docs/api-guide/relations.md @@ -134,7 +134,7 @@ By default this field is read-write, although you can change this behavior using  **Arguments**: -* `view_name` - The view name that should be used as the target of the relationship.  **required**. +* `view_name` - The view name that should be used as the target of the relationship.  If you're using [the standard router classes][routers] this wil be a string with the format `<modelname>-detail`. **required**.  * `many` - If applied to a to-many relationship, you should set this argument to `True`.  * `required` - If set to `False`, the field will accept values of `None` or the empty-string for nullable relationships.  * `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`. @@ -202,7 +202,7 @@ This field is always read-only.  **Arguments**: -* `view_name` - The view name that should be used as the target of the relationship.  **required**. +* `view_name` - The view name that should be used as the target of the relationship.  If you're using [the standard router classes][routers] this wil be a string with the format `<model_name>-detail`.  **required**.  * `lookup_field` - The field on the target that should be used for the lookup.  Should correspond to a URL keyword argument on the referenced view.  Default is `'pk'`.  * `format` - If using format suffixes, hyperlinked fields will use the same format suffix for the target unless overridden by using the `format` argument. @@ -454,6 +454,7 @@ The [drf-nested-routers package][drf-nested-routers] provides routers and relati  [cite]: http://lwn.net/Articles/193245/  [reverse-relationships]: https://docs.djangoproject.com/en/dev/topics/db/queries/#following-relationships-backward +[routers]: http://www.django-rest-framework.org/api-guide/routers#defaultrouter  [generic-relations]: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/#id1  [2.2-announcement]: ../topics/2.2-announcement.md  [drf-nested-routers]: https://github.com/alanjds/drf-nested-routers diff --git a/docs/api-guide/routers.md b/docs/api-guide/routers.md index 249e99a4..819cf980 100644 --- a/docs/api-guide/routers.md +++ b/docs/api-guide/routers.md @@ -37,7 +37,19 @@ The example above would generate the following URL patterns:  * URL pattern: `^accounts/$`  Name: `'account-list'`  * URL pattern: `^accounts/{pk}/$`  Name: `'account-detail'` -### Registering additional routes +--- + +**Note**: The `base_name` argument is used to specify the initial part of the view name pattern.  In the example above, that's the `user` or `account` part. + +Typically you won't *need* to specify the `base-name` argument, but if you have a viewset where you've defined a custom `get_queryset` method, then the viewset may not have any `.model` or `.queryset` attribute set.  If you try to register that viewset you'll see an error like this: + +    'base_name' argument not specified, and could not automatically determine the name from the viewset, as it does not have a '.model' or '.queryset' attribute. + +This means you'll need to explicitly set the `base_name` argument when registering the viewset, as it could not be automatically determined from the model name. + +--- + +### Extra link and actions  Any methods on the viewset decorated with `@detail_route` or `@list_route` will also be routed.  For example, given a method like this on the `UserViewSet` class: diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 6fc25f57..7ee060af 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -103,11 +103,11 @@ Deserialization is similar.  First we parse a stream into Python native datatype  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` +    serializer = CommentSerializer(comment, data=data)  # Update `comment`  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 +    serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)  # Update `comment` with partial data  ## Validation @@ -161,7 +161,7 @@ To do any other validation that requires access to multiple fields, add a method              """              Check that the start is before the stop.              """ -            if attrs['start'] < attrs['finish']: +            if attrs['start'] > attrs['finish']:                  raise serializers.ValidationError("finish must occur after start")              return attrs @@ -208,7 +208,7 @@ Similarly if a nested representation should be a list of items, you should pass  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. -    serializer = CommentSerializer(comment, data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'}) +    serializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})      serializer.is_valid()      # False      serializer.errors @@ -373,6 +373,25 @@ You may wish to specify multiple fields as read-only.  Instead of adding each fi  Model fields which have `editable=False` set, and `AutoField` fields will be set to read-only by default, and do not need to be added to the `read_only_fields` option.  +## Specifying which fields should be write-only  + +You may wish to specify multiple fields as write-only.  Instead of adding each field explicitly with the `write_only=True` attribute, you may use the `write_only_fields` Meta option, like so: + +    class CreateUserSerializer(serializers.ModelSerializer): +        class Meta: +            model = User +            fields = ('email', 'username', 'password') +            write_only_fields = ('password',)  # Note: Password field is write-only + +        def restore_object(self, attrs, instance=None): +            """ +            Instantiate a new User instance. +            """ +            assert instance is None, 'Cannot update users with CreateUserSerializer'                                 +            user = User(email=attrs['email'], username=attrs['username']) +            user.set_password(attrs['password']) +            return user +   ## Specifying fields explicitly   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. @@ -445,6 +464,29 @@ For more specific requirements such as specifying a different lookup for each fi              model = Account              fields = ('url', 'account_name', 'users', 'created') +## Overiding the URL field behavior + +The name of the URL field defaults to 'url'.  You can override this globally, by using the `URL_FIELD_NAME` setting. + +You can also override this on a per-serializer basis by using the `url_field_name` option on the serializer, like so: + +    class AccountSerializer(serializers.HyperlinkedModelSerializer): +        class Meta: +            model = Account +            fields = ('account_url', 'account_name', 'users', 'created') +            url_field_name = 'account_url' + +**Note**: The generic view implementations normally generate a `Location` header in response to successful `POST` requests.  Serializers using `url_field_name` option will not have this header automatically included by the view.  If you need to do so you will ned to also override the view's `get_success_headers()` method. + +You can also overide the URL field's view name and lookup field without overriding the field explicitly, by using the `view_name` and `lookup_field` options, like so: + +    class AccountSerializer(serializers.HyperlinkedModelSerializer): +        class Meta: +            model = Account +            fields = ('account_url', 'account_name', 'users', 'created') +            view_name = 'account_detail' +            lookup_field='account_name' +  ---  # Advanced serializer usage diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index d8c878ff..8bde4d87 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -158,6 +158,18 @@ A client request like the following would return a paginated list of up to 100 i  Default: `None` +### SEARCH_PARAM + +The name of a query paramater, which can be used to specify the search term used by `SearchFilter`. + +Default: `search` + +#### ORDERING_PARAM + +The name of a query paramater, which can be used to specify the ordering of results returned by `OrderingFilter`. + +Default: `ordering` +  ---  ## Authentication settings @@ -353,6 +365,12 @@ This should be a function with the following signature:  Default: `'rest_framework.views.exception_handler'` +#### URL_FIELD_NAME + +A string representing the key that should be used for the URL fields generated by `HyperlinkedModelSerializer`. + +Default: `'url'` +  #### FORMAT_SUFFIX_KWARG  The name of a parameter in the URL conf that may be used to provide a format suffix. diff --git a/docs/api-guide/testing.md b/docs/api-guide/testing.md index 4a8a9168..72c33961 100644 --- a/docs/api-guide/testing.md +++ b/docs/api-guide/testing.md @@ -218,12 +218,12 @@ You can use any of REST framework's test case classes as you would for the regul  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`: +For example, it's easier to inspect `response.data`:      response = self.client.get('/users/4/')      self.assertEqual(response.data, {'id': 4, 'username': 'lauren'}) -Instead of inspecting the result of parsing `request.content`: +Instead of inspecting the result of parsing `response.content`:      response = self.client.get('/users/4/')      self.assertEqual(json.loads(response.content), {'id': 4, 'username': 'lauren'}) diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index bedecb36..d223f9b3 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -160,7 +160,7 @@ For example, given the following views...      REST_FRAMEWORK = {          'DEFAULT_THROTTLE_CLASSES': ( -            'rest_framework.throttling.ScopedRateThrottle' +            'rest_framework.throttling.ScopedRateThrottle',          ),          'DEFAULT_THROTTLE_RATES': {              'contacts': '1000/day', diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md index dfd9d22a..161fd2a9 100644 --- a/docs/api-guide/viewsets.md +++ b/docs/api-guide/viewsets.md @@ -233,7 +233,7 @@ To create a base viewset class that provides `create`, `list` and `retrieve` ope                                      mixins.RetrieveModelMixin,                                      viewsets.GenericViewSet):          """ -        A viewset that provides `retrieve`, `update`, and `list` actions. +        A viewset that provides `retrieve`, `create`, and `list` actions.          To use it, override the class and set the `.queryset` and          `.serializer_class` attributes. | 
