aboutsummaryrefslogtreecommitdiffstats
path: root/docs/topics
diff options
context:
space:
mode:
Diffstat (limited to 'docs/topics')
-rw-r--r--docs/topics/2.2-announcement.md18
-rw-r--r--docs/topics/2.3-announcement.md34
-rw-r--r--docs/topics/2.4-announcement.md172
-rw-r--r--docs/topics/3.0-announcement.md965
-rw-r--r--docs/topics/3.1-announcement.md209
-rw-r--r--docs/topics/ajax-csrf-cors.md8
-rw-r--r--docs/topics/browsable-api.md45
-rw-r--r--docs/topics/contributing.md155
-rw-r--r--docs/topics/credits.md340
-rw-r--r--docs/topics/documenting-your-api.md18
-rw-r--r--docs/topics/internationalization.md113
-rw-r--r--docs/topics/kickstarter-announcement.md163
-rw-r--r--docs/topics/project-management.md200
-rw-r--r--docs/topics/release-notes.md712
-rw-r--r--docs/topics/rest-framework-2-announcement.md8
-rw-r--r--docs/topics/rest-hypermedia-hateoas.md12
-rw-r--r--docs/topics/third-party-resources.md328
-rw-r--r--docs/topics/writable-nested-serializers.md47
18 files changed, 2586 insertions, 961 deletions
diff --git a/docs/topics/2.2-announcement.md b/docs/topics/2.2-announcement.md
index 02cac129..e6220f42 100644
--- a/docs/topics/2.2-announcement.md
+++ b/docs/topics/2.2-announcement.md
@@ -1,4 +1,4 @@
-# REST framework 2.2 announcement
+# Django REST framework 2.2
The 2.2 release represents an important point for REST framework, with the addition of Python 3 support, and the introduction of an official deprecation policy.
@@ -42,7 +42,7 @@ The 2.2 release makes a few changes to the API, in order to make it more consist
The `ManyRelatedField()` style is being deprecated in favor of a new `RelatedField(many=True)` syntax.
-For example, if a user is associated with multiple questions, which we want to represent using a primary key relationship, we might use something like the following:
+For example, if a user is associated with multiple questions, which we want to represent using a primary key relationship, we might use something like the following:
class UserSerializer(serializers.HyperlinkedModelSerializer):
questions = serializers.PrimaryKeyRelatedField(many=True)
@@ -58,10 +58,10 @@ The change also applies to serializers. If you have a nested serializer, you sh
class Meta:
model = Track
fields = ('name', 'duration')
-
+
class AlbumSerializer(serializer.ModelSerializer):
tracks = TrackSerializer(many=True)
-
+
class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')
@@ -87,7 +87,7 @@ For example, is a user account has an optional foreign key to a company, that yo
This is in line both with the rest of the serializer fields API, and with Django's `Form` and `ModelForm` API.
-Using `required` throughout the serializers API means you won't need to consider if a particular field should take `blank` or `null` arguments instead of `required`, and also means there will be more consistent behavior for how fields are treated when they are not present in the incoming data.
+Using `required` throughout the serializers API means you won't need to consider if a particular field should take `blank` or `null` arguments instead of `required`, and also means there will be more consistent behavior for how fields are treated when they are not present in the incoming data.
The `null=True` argument will continue to function, and will imply `required=False`, but will raise a `PendingDeprecationWarning`.
@@ -136,22 +136,22 @@ Now becomes:
def has_object_permission(self, request, view, obj):
return obj.owner == request.user
-If you're overriding the `BasePermission` class, the old-style signature will continue to function, and will correctly handle both global and object-level permissions checks, but it's use will raise a `PendingDeprecationWarning`.
+If you're overriding the `BasePermission` class, the old-style signature will continue to function, and will correctly handle both global and object-level permissions checks, but its use will raise a `PendingDeprecationWarning`.
Note also that the usage of the internal APIs for permission checking on the `View` class has been cleaned up slightly, and is now documented and subject to the deprecation policy in all future versions.
### More explicit hyperlink relations behavior
-When using a serializer with a `HyperlinkedRelatedField` or `HyperlinkedIdentityField`, the hyperlinks would previously use absolute URLs if the serializer context included a `'request'` key, and fallback to using relative URLs otherwise. This could lead to non-obvious behavior, as it might not be clear why some serializers generated absolute URLs, and others do not.
+When using a serializer with a `HyperlinkedRelatedField` or `HyperlinkedIdentityField`, the hyperlinks would previously use absolute URLs if the serializer context included a `'request'` key, and fall back to using relative URLs otherwise. This could lead to non-obvious behavior, as it might not be clear why some serializers generated absolute URLs, and others do not.
-From version 2.2 onwards, serializers with hyperlinked relationships *always* require a `'request'` key to be supplied in the context dictionary. The implicit behavior will continue to function, but it's use will raise a `PendingDeprecationWarning`.
+From version 2.2 onwards, serializers with hyperlinked relationships *always* require a `'request'` key to be supplied in the context dictionary. The implicit behavior will continue to function, but its use will raise a `PendingDeprecationWarning`.
[xordoquy]: https://github.com/xordoquy
[django-python-3]: https://docs.djangoproject.com/en/dev/faq/install/#can-i-use-django-with-python-3
[porting-python-3]: https://docs.djangoproject.com/en/dev/topics/python3/
[python-compat]: https://docs.djangoproject.com/en/dev/releases/1.5/#python-compatibility
[django-deprecation-policy]: https://docs.djangoproject.com/en/dev/internals/release-process/#internal-release-deprecation-policy
-[credits]: http://django-rest-framework.org/topics/credits.html
+[credits]: http://www.django-rest-framework.org/topics/credits
[mailing-list]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework
[django-rest-framework-docs]: https://github.com/marcgibbons/django-rest-framework-docs
[marcgibbons]: https://github.com/marcgibbons/
diff --git a/docs/topics/2.3-announcement.md b/docs/topics/2.3-announcement.md
index 9fdebcd9..21d9f1db 100644
--- a/docs/topics/2.3-announcement.md
+++ b/docs/topics/2.3-announcement.md
@@ -1,4 +1,4 @@
-# REST framework 2.3 announcement
+# Django REST framework 2.3
REST framework 2.3 makes it even quicker and easier to build your Web APIs.
@@ -15,7 +15,7 @@ As an example of just how simple REST framework APIs can now be, here's an API w
"""
A REST framework API for viewing and editing users and groups.
"""
- from django.conf.urls.defaults import url, patterns, include
+ from django.conf.urls.defaults import url, include
from django.contrib.auth.models import User, Group
from rest_framework import viewsets, routers
@@ -27,7 +27,7 @@ As an example of just how simple REST framework APIs can now be, here's an API w
class GroupViewSet(viewsets.ModelViewSet):
model = Group
-
+
# Routers provide an easy way of automatically determining the URL conf
router = routers.DefaultRouter()
router.register(r'users', UserViewSet)
@@ -35,11 +35,11 @@ As an example of just how simple REST framework APIs can now be, here's an API w
# Wire up our API using automatic URL routing.
- # Additionally, we include login URLs for the browseable API.
- urlpatterns = patterns('',
+ # Additionally, we include login URLs for the browsable API.
+ urlpatterns = [
url(r'^', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
- )
+ ]
The best place to get started with ViewSets and Routers is to take a look at the [newest section in the tutorial][part-6], which demonstrates their usage.
@@ -131,7 +131,7 @@ The `get_object` and `get_paginate_by` methods no longer take an optional querys
Using an optional queryset with these methods continues to be supported, but will raise a `PendingDeprecationWarning`.
-The `paginate_queryset` method no longer takes a `page_size` argument, or returns a four-tuple of pagination information. Instead it simply takes a queryset argument, and either returns a `page` object with an appropraite page size, or returns `None`, if pagination is not configured for the view.
+The `paginate_queryset` method no longer takes a `page_size` argument, or returns a four-tuple of pagination information. Instead it simply takes a queryset argument, and either returns a `page` object with an appropriate page size, or returns `None`, if pagination is not configured for the view.
Using the `page_size` argument is still supported and will trigger the old-style return type, but will raise a `PendingDeprecationWarning`.
@@ -195,23 +195,23 @@ Usage of the old-style attributes continues to be supported, but will raise a `P
2.3 introduces a `DecimalField` serializer field, which returns `Decimal` instances.
-For most cases APIs using model fields will behave as previously, however if you are using a custom renderer, not provided by REST framework, then you may now need to add support for rendering `Decimal` instances to your renderer implmentation.
+For most cases APIs using model fields will behave as previously, however if you are using a custom renderer, not provided by REST framework, then you may now need to add support for rendering `Decimal` instances to your renderer implementation.
-## ModelSerializers and reverse relationships
+## ModelSerializers and reverse relationships
The support for adding reverse relationships to the `fields` option on a `ModelSerializer` class means that the `get_related_field` and `get_nested_field` method signatures have now changed.
-In the unlikely event that you're providing a custom serializer class, and implementing these methods you should note the new call signature for both methods is now `(self, model_field, related_model, to_many)`. For revese relationships `model_field` will be `None`.
+In the unlikely event that you're providing a custom serializer class, and implementing these methods you should note the new call signature for both methods is now `(self, model_field, related_model, to_many)`. For reverse relationships `model_field` will be `None`.
-The old-style signature will continue to function but will raise a `PendingDeprecationWarning`.
+The old-style signature will continue to function but will raise a `PendingDeprecationWarning`.
## View names and descriptions
-The mechanics of how the names and descriptions used in the browseable API are generated has been modified and cleaned up somewhat.
+The mechanics of how the names and descriptions used in the browsable API are generated has been modified and cleaned up somewhat.
-If you've been customizing this behavior, for example perhaps to use `rst` markup for the browseable API, then you'll need to take a look at the implementation to see what updates you need to make.
+If you've been customizing this behavior, for example perhaps to use `rst` markup for the browsable API, then you'll need to take a look at the implementation to see what updates you need to make.
-Note that the relevant methods have always been private APIs, and the docstrings called them out as intended to be deprecated.
+Note that the relevant methods have always been private APIs, and the docstrings called them out as intended to be deprecated.
---
@@ -219,7 +219,7 @@ Note that the relevant methods have always been private APIs, and the docstrings
## More explicit style
-The usage of `model` attribute in generic Views is still supported, but it's usage is generally being discouraged throughout the documentation, in favour of the setting the more explict `queryset` and `serializer_class` attributes.
+The usage of `model` attribute in generic Views is still supported, but it's usage is generally being discouraged throughout the documentation, in favour of the setting the more explicit `queryset` and `serializer_class` attributes.
For example, the following is now the recommended style for using generic views:
@@ -227,7 +227,7 @@ For example, the following is now the recommended style for using generic views:
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
-Using an explict `queryset` and `serializer_class` attributes makes the functioning of the view more clear than using the shortcut `model` attribute.
+Using an explicit `queryset` and `serializer_class` attributes makes the functioning of the view more clear than using the shortcut `model` attribute.
It also makes the usage of the `get_queryset()` or `get_serializer_class()` methods more obvious.
@@ -246,7 +246,7 @@ It also makes the usage of the `get_queryset()` or `get_serializer_class()` meth
## Django 1.3 support
-The 2.3.x release series will be the last series to provide compatiblity with Django 1.3.
+The 2.3.x release series will be the last series to provide compatibility with Django 1.3.
## Version 2.2 API changes
diff --git a/docs/topics/2.4-announcement.md b/docs/topics/2.4-announcement.md
new file mode 100644
index 00000000..4ca35290
--- /dev/null
+++ b/docs/topics/2.4-announcement.md
@@ -0,0 +1,172 @@
+# Django REST framework 2.4
+
+The 2.4 release is largely an intermediate step, tying up some outstanding issues prior to the 3.x series.
+
+## Version requirements
+
+Support for Django 1.3 has been dropped.
+The lowest supported version of Django is now 1.4.2.
+
+The current plan is for REST framework to remain in lockstep with [Django's long-term support policy][lts-releases].
+
+## Django 1.7 support
+
+The optional authtoken application now includes support for *both* Django 1.7 schema migrations, *and* for old-style `south` migrations.
+
+**If you are using authtoken, and you want to continue using `south`, you must upgrade your `south` package to version 1.0.**
+
+## Deprecation of `.model` view attribute
+
+The `.model` attribute on view classes is an optional shortcut for either or both of `.serializer_class` and `.queryset`. Its usage results in more implicit, less obvious behavior.
+
+The documentation has previously stated that usage of the more explicit style is prefered, and we're now taking that one step further and deprecating the usage of the `.model` shortcut.
+
+Doing so will mean that there are cases of API code where you'll now need to include a serializer class where you previously were just using the `.model` shortcut. However we firmly believe that it is the right trade-off to make.
+
+Removing the shortcut takes away an unnecessary layer of abstraction, and makes your codebase more explicit without any significant extra complexity. It also results in better consistency, as there's now only one way to set the serializer class and queryset attributes for the view, instead of two.
+
+The `DEFAULT_MODEL_SERIALIZER_CLASS` API setting is now also deprecated.
+
+## Updated test runner
+
+We now have a new test runner for developing against the project,, that uses the excellent [py.test](http://pytest.org) library.
+
+To use it make sure you have first installed the test requirements.
+
+ pip install -r requirements-test.txt
+
+Then run the `runtests.py` script.
+
+ ./runtests.py
+
+The new test runner also includes [flake8](https://flake8.readthedocs.org) code linting, which should help keep our coding style consistent.
+
+#### Test runner flags
+
+Run using a more concise output style.
+
+ ./runtests -q
+
+Run the tests using a more concise output style, no coverage, no flake8.
+
+ ./runtests --fast
+
+Don't run the flake8 code linting.
+
+ ./runtests --nolint
+
+Only run the flake8 code linting, don't run the tests.
+
+ ./runtests --lintonly
+
+Run the tests for a given test case.
+
+ ./runtests MyTestCase
+
+Run the tests for a given test method.
+
+ ./runtests MyTestCase.test_this_method
+
+Shorter form to run the tests for a given test method.
+
+ ./runtests test_this_method
+
+Note: The test case and test method matching is fuzzy and will sometimes run other tests that contain a partial string match to the given command line input.
+
+## Improved viewset routing
+
+The `@action` and `@link` decorators were inflexible in that they only allowed additional routes to be added against instance style URLs, not against list style URLs.
+
+The `@action` and `@link` decorators have now been moved to pending deprecation, and the `@list_route` and `@detail_route` decorators have been introduced.
+
+Here's an example of using the new decorators. Firstly we have a detail-type route named "set_password" that acts on a single instance, and takes a `pk` argument in the URL. Secondly we have a list-type route named "recent_users" that acts on a queryset, and does not take any arguments in the URL.
+
+ class UserViewSet(viewsets.ModelViewSet):
+ """
+ A viewset that provides the standard actions
+ """
+ queryset = User.objects.all()
+ serializer_class = UserSerializer
+
+ @detail_route(methods=['post'])
+ def set_password(self, request, pk=None):
+ user = self.get_object()
+ serializer = PasswordSerializer(data=request.DATA)
+ if serializer.is_valid():
+ user.set_password(serializer.data['password'])
+ user.save()
+ return Response({'status': 'password set'})
+ else:
+ return Response(serializer.errors,
+ status=status.HTTP_400_BAD_REQUEST)
+
+ @list_route()
+ def recent_users(self, request):
+ recent_users = User.objects.all().order('-last_login')
+ page = self.paginate_queryset(recent_users)
+ serializer = self.get_pagination_serializer(page)
+ return Response(serializer.data)
+
+For more details, see the [viewsets documentation](../api-guide/viewsets.md).
+
+## Throttle behavior
+
+There's one bugfix in 2.4 that's worth calling out, as it will *invalidate existing throttle caches* when you upgrade.
+
+We've now fixed a typo on the `cache_format` attribute. Previously this was named `"throtte_%(scope)s_%(ident)s"`, it is now `"throttle_%(scope)s_%(ident)s"`.
+
+If you're concerned about the invalidation you have two options.
+
+* Manually pre-populate your cache with the fixed version.
+* Set the `cache_format` attribute on your throttle class in order to retain the previous incorrect spelling.
+
+## Other features
+
+There are also a number of other features and bugfixes as [listed in the release notes][2-4-release-notes]. In particular these include:
+
+[Customizable view name and description functions][view-name-and-description-settings] for use with the browsable API, by using the `VIEW_NAME_FUNCTION` and `VIEW_DESCRIPTION_FUNCTION` settings.
+
+Smarter [client IP identification for throttling][client-ip-identification], with the addition of the `NUM_PROXIES` setting.
+
+Added the standardized `Retry-After` header to throttled responses, as per [RFC 6585](http://tools.ietf.org/html/rfc6585). This should now be used in preference to the custom `X-Throttle-Wait-Seconds` header which will be fully deprecated in 3.0.
+
+## Deprecations
+
+All API changes in 2.3 that previously raised `PendingDeprecationWarning` will now raise a `DeprecationWarning`, which is loud by default.
+
+All API changes in 2.3 that previously raised `DeprecationWarning` have now been removed entirely.
+
+Furter details on these deprecations is available in the [2.3 announcement][2-3-announcement].
+
+## Labels and milestones
+
+Although not strictly part of the 2.4 release it's also worth noting here that we've been working hard towards improving our triage process.
+
+The [labels that we use in GitHub][github-labels] have been cleaned up, and all existing tickets triaged. Any given ticket should have one and only one label, indicating its current state.
+
+We've also [started using milestones][github-milestones] in order to track tickets against particular releases.
+
+---
+
+![Labels and milestones](../img/labels-and-milestones.png)
+
+**Above**: *Overview of our current use of labels and milestones in GitHub.*
+
+---
+
+We hope both of these changes will help make the management process more clear and obvious and help keep tickets well-organised and relevant.
+
+## Next steps
+
+The next planned release will be 3.0, featuring an improved and simplified serializer implementation.
+
+Once again, many thanks to all the generous [backers and sponsors][kickstarter-sponsors] who've helped make this possible!
+
+[lts-releases]: https://docs.djangoproject.com/en/dev/internals/release-process/#long-term-support-lts-releases
+[2-4-release-notes]: release-notes#240
+[view-name-and-description-settings]: ../api-guide/settings#view-names-and-descriptions
+[client-ip-identification]: ../api-guide/throttling#how-clients-are-identified
+[2-3-announcement]: 2.3-announcement
+[github-labels]: https://github.com/tomchristie/django-rest-framework/issues
+[github-milestones]: https://github.com/tomchristie/django-rest-framework/milestones
+[kickstarter-sponsors]: kickstarter-announcement#sponsors
diff --git a/docs/topics/3.0-announcement.md b/docs/topics/3.0-announcement.md
new file mode 100644
index 00000000..59fe779c
--- /dev/null
+++ b/docs/topics/3.0-announcement.md
@@ -0,0 +1,965 @@
+# Django REST framework 3.0
+
+The 3.0 release of Django REST framework is the result of almost four years of iteration and refinement. It comprehensively addresses some of the previous remaining design issues in serializers, fields and the generic views.
+
+**This release is incremental in nature. There *are* some breaking API changes, and upgrading *will* require you to read the release notes carefully, but the migration path should otherwise be relatively straightforward.**
+
+The difference in quality of the REST framework API and implementation should make writing, maintaining and debugging your application far easier.
+
+3.0 is the first of three releases that have been funded by our recent [Kickstarter campaign][kickstarter].
+
+As ever, a huge thank you to our many [wonderful sponsors][sponsors]. If you're looking for a Django gig, and want to work with smart community-minded folks, you should probably check out that list and see who's hiring.
+
+---
+
+## New features
+
+Notable features of this new release include:
+
+* Printable representations on serializers that allow you to inspect exactly what fields are present on the instance.
+* Simple model serializers that are vastly easier to understand and debug, and that make it easy to switch between the implicit `ModelSerializer` class and the explicit `Serializer` class.
+* A new `BaseSerializer` class, making it easier to write serializers for alternative storage backends, or to completely customize your serialization and validation logic.
+* A cleaner fields API including new classes such as `ListField` and `MultipleChoiceField`.
+* [Super simple default implementations][mixins.py] for the generic views.
+* Support for overriding how validation errors are handled by your API.
+* A metadata API that allows you to customize how `OPTIONS` requests are handled by your API.
+* A more compact JSON output with unicode style encoding turned on by default.
+* Templated based HTML form rendering for serializers. This will be finalized as public API in the upcoming 3.1 release.
+
+Significant new functionality continues to be planned for the 3.1 and 3.2 releases. These releases will correspond to the two [Kickstarter stretch goals](https://www.kickstarter.com/projects/tomchristie/django-rest-framework-3) - "Feature improvements" and "Admin interface". Further 3.x releases will present simple upgrades, without the same level of fundamental API changes necessary for the 3.0 release.
+
+---
+
+#### REST framework: Under the hood.
+
+This talk from the [Django: Under the Hood](http://www.djangounderthehood.com/) event in Amsterdam, Nov 2014, gives some good background context on the design decisions behind 3.0.
+
+<iframe style="display: block; margin: 0 auto 0 auto" width="560" height="315" src="//www.youtube.com/embed/3cSsbe-tA0E" frameborder="0" allowfullscreen></iframe>
+
+---
+
+*Below is an in-depth guide to the API changes and migration notes for 3.0.*
+
+## Request objects
+
+#### The `.data` and `.query_params` properties.
+
+The usage of `request.DATA` and `request.FILES` is now pending deprecation in favor of a single `request.data` attribute that contains *all* the parsed data.
+
+Having separate attributes is reasonable for web applications that only ever parse url-encoded or multipart requests, but makes less sense for the general-purpose request parsing that REST framework supports.
+
+You may now pass all the request data to a serializer class in a single argument:
+
+ # Do this...
+ ExampleSerializer(data=request.data)
+
+Instead of passing the files argument separately:
+
+ # Don't do this...
+ ExampleSerializer(data=request.DATA, files=request.FILES)
+
+
+The usage of `request.QUERY_PARAMS` is now pending deprecation in favor of the lowercased `request.query_params`.
+
+---
+
+## Serializers
+
+#### Single-step object creation.
+
+Previously the serializers used a two-step object creation, as follows:
+
+1. Validating the data would create an object instance. This instance would be available as `serializer.object`.
+2. Calling `serializer.save()` would then save the object instance to the database.
+
+This style is in-line with how the `ModelForm` class works in Django, but is problematic for a number of reasons:
+
+* Some data, such as many-to-many relationships, cannot be added to the object instance until after it has been saved. This type of data needed to be hidden in some undocumented state on the object instance, or kept as state on the serializer instance so that it could be used when `.save()` is called.
+* Instantiating model instances directly means that you cannot use model manager classes for instance creation, e.g. `ExampleModel.objects.create(...)`. Manager classes are an excellent layer at which to enforce business logic and application-level data constraints.
+* The two step process makes it unclear where to put deserialization logic. For example, should extra attributes such as the current user get added to the instance during object creation or during object save?
+
+We now use single-step object creation, like so:
+
+1. Validating the data makes the cleaned data available as `serializer.validated_data`.
+2. Calling `serializer.save()` then saves and returns the new object instance.
+
+The resulting API changes are further detailed below.
+
+#### The `.create()` and `.update()` methods.
+
+The `.restore_object()` method is now removed, and we instead have two separate methods, `.create()` and `.update()`. These methods work slightly different to the previous `.restore_object()`.
+
+When using the `.create()` and `.update()` methods you should both create *and save* the object instance. This is in contrast to the previous `.restore_object()` behavior that would instantiate the object but not save it.
+
+These methods also replace the optional `.save_object()` method, which no longer exists.
+
+The following example from the tutorial previously used `restore_object()` to handle both creating and updating object instances.
+
+ def restore_object(self, attrs, instance=None):
+ if instance:
+ # Update existing instance
+ instance.title = attrs.get('title', instance.title)
+ instance.code = attrs.get('code', instance.code)
+ instance.linenos = attrs.get('linenos', instance.linenos)
+ instance.language = attrs.get('language', instance.language)
+ instance.style = attrs.get('style', instance.style)
+ return instance
+
+ # Create new instance
+ return Snippet(**attrs)
+
+This would now be split out into two separate methods.
+
+ def update(self, instance, validated_data):
+ instance.title = validated_data.get('title', instance.title)
+ instance.code = validated_data.get('code', instance.code)
+ instance.linenos = validated_data.get('linenos', instance.linenos)
+ instance.language = validated_data.get('language', instance.language)
+ instance.style = validated_data.get('style', instance.style)
+ instance.save()
+ return instance
+
+ def create(self, validated_data):
+ return Snippet.objects.create(**validated_data)
+
+Note that these methods should return the newly created object instance.
+
+#### Use `.validated_data` instead of `.object`.
+
+You must now use the `.validated_data` attribute if you need to inspect the data before saving, rather than using the `.object` attribute, which no longer exists.
+
+For example the following code *is no longer valid*:
+
+ if serializer.is_valid():
+ name = serializer.object.name # Inspect validated field data.
+ logging.info('Creating ticket "%s"' % name)
+ serializer.object.user = request.user # Include the user when saving.
+ serializer.save()
+
+Instead of using `.object` to inspect a partially constructed instance, you would now use `.validated_data` to inspect the cleaned incoming values. Also you can't set extra attributes on the instance directly, but instead pass them to the `.save()` method as keyword arguments.
+
+The corresponding code would now look like this:
+
+ if serializer.is_valid():
+ name = serializer.validated_data['name'] # Inspect validated field data.
+ logging.info('Creating ticket "%s"' % name)
+ serializer.save(user=request.user) # Include the user when saving.
+
+#### Using `.is_valid(raise_exception=True)`
+
+The `.is_valid()` method now takes an optional boolean flag, `raise_exception`.
+
+Calling `.is_valid(raise_exception=True)` will cause a `ValidationError` to be raised if the serializer data contains validation errors. This error will be handled by REST framework's default exception handler, allowing you to remove error response handling from your view code.
+
+The handling and formatting of error responses may be altered globally by using the `EXCEPTION_HANDLER` settings key.
+
+This change also means it's now possible to alter the style of error responses used by the built-in generic views, without having to include mixin classes or other overrides.
+
+#### Using `serializers.ValidationError`.
+
+Previously `serializers.ValidationError` error was simply a synonym for `django.core.exceptions.ValidationError`. This has now been altered so that it inherits from the standard `APIException` base class.
+
+The reason behind this is that Django's `ValidationError` class is intended for use with HTML forms and its API makes using it slightly awkward with nested validation errors that can occur in serializers.
+
+For most users this change shouldn't require any updates to your codebase, but it is worth ensuring that whenever raising validation errors you should prefer using the `serializers.ValidationError` exception class, and not Django's built-in exception.
+
+We strongly recommend that you use the namespaced import style of `import serializers` and not `from serializers import ValidationError` in order to avoid any potential confusion.
+
+#### Change to `validate_<field_name>`.
+
+The `validate_<field_name>` method hooks that can be attached to serializer classes change their signature slightly and return type. Previously these would take a dictionary of all incoming data, and a key representing the field name, and would return a dictionary including the validated data for that field:
+
+ def validate_score(self, attrs, source):
+ if attrs['score'] % 10 != 0:
+ raise serializers.ValidationError('This field should be a multiple of ten.')
+ return attrs
+
+This is now simplified slightly, and the method hooks simply take the value to be validated, and return the validated value.
+
+ def validate_score(self, value):
+ if value % 10 != 0:
+ raise serializers.ValidationError('This field should be a multiple of ten.')
+ return value
+
+Any ad-hoc validation that applies to more than one field should go in the `.validate(self, attrs)` method as usual.
+
+Because `.validate_<field_name>` would previously accept the complete dictionary of attributes, it could be used to validate a field depending on the input in another field. Now if you need to do this you should use `.validate()` instead.
+
+You can either return `non_field_errors` from the validate method by raising a simple `ValidationError`
+
+ def validate(self, attrs):
+ # serializer.errors == {'non_field_errors': ['A non field error']}
+ raise serializers.ValidationError('A non field error')
+
+Alternatively if you want the errors to be against a specific field, use a dictionary of when instantiating the `ValidationError`, like so:
+
+ def validate(self, attrs):
+ # serializer.errors == {'my_field': ['A field error']}
+ raise serializers.ValidationError({'my_field': 'A field error'})
+
+This ensures you can still write validation that compares all the input fields, but that marks the error against a particular field.
+
+#### Removal of `transform_<field_name>`.
+
+The under-used `transform_<field_name>` on serializer classes is no longer provided. Instead you should just override `to_representation()` if you need to apply any modifications to the representation style.
+
+For example:
+
+ def to_representation(self, instance):
+ ret = super(UserSerializer, self).to_representation(instance)
+ ret['username'] = ret['username'].lower()
+ return ret
+
+Dropping the extra point of API means there's now only one right way to do things. This helps with repetition and reinforcement of the core API, rather than having multiple differing approaches.
+
+If you absolutely need to preserve `transform_<field_name>` behavior, for example, in order to provide a simpler 2.x to 3.0 upgrade, you can use a mixin, or serializer base class that add the behavior back in. For example:
+
+ class BaseModelSerializer(ModelSerializer):
+ """
+ A custom ModelSerializer class that preserves 2.x style `transform_<field_name>` behavior.
+ """
+ def to_representation(self, instance):
+ ret = super(BaseModelSerializer, self).to_representation(instance)
+ for key, value in ret.items():
+ method = getattr(self, 'transform_' + key, None)
+ if method is not None:
+ ret[key] = method(value)
+ return ret
+
+#### Differences between ModelSerializer validation and ModelForm.
+
+This change also means that we no longer use the `.full_clean()` method on model instances, but instead perform all validation explicitly on the serializer. This gives a cleaner separation, and ensures that there's no automatic validation behavior on `ModelSerializer` classes that can't also be easily replicated on regular `Serializer` classes.
+
+For the most part this change should be transparent. Field validation and uniqueness checks will still be run as normal, but the implementation is a little different.
+
+The one difference that you do need to note is that the `.clean()` method will not be called as part of serializer validation, as it would be if using a `ModelForm`. Use the serializer `.validate()` method to perform a final validation step on incoming data where required.
+
+There may be some cases where you really do need to keep validation logic in the model `.clean()` method, and cannot instead separate it into the serializer `.validate()`. You can do so by explicitly instantiating a model instance in the `.validate()` method.
+
+ def validate(self, attrs):
+ instance = ExampleModel(**attrs)
+ instance.clean()
+ return attrs
+
+Again, you really should look at properly separating the validation logic out of the model method if possible, but the above might be useful in some backwards compatibility cases, or for an easy migration path.
+
+#### Writable nested serialization.
+
+REST framework 2.x attempted to automatically support writable nested serialization, but the behavior was complex and non-obvious. Attempting to automatically handle these case is problematic:
+
+* There can be complex dependencies involved in order of saving multiple related model instances.
+* It's unclear what behavior the user should expect when related models are passed `None` data.
+* It's unclear how the user should expect to-many relationships to handle updates, creations and deletions of multiple records.
+
+Using the `depth` option on `ModelSerializer` will now create **read-only nested serializers** by default.
+
+If you try to use a writable nested serializer without writing a custom `create()` and/or `update()` method you'll see an assertion error when you attempt to save the serializer. For example:
+
+ >>> class ProfileSerializer(serializers.ModelSerializer):
+ >>> class Meta:
+ >>> model = Profile
+ >>> fields = ('address', 'phone')
+ >>>
+ >>> class UserSerializer(serializers.ModelSerializer):
+ >>> profile = ProfileSerializer()
+ >>> class Meta:
+ >>> model = User
+ >>> fields = ('username', 'email', 'profile')
+ >>>
+ >>> data = {
+ >>> 'username': 'lizzy',
+ >>> 'email': 'lizzy@example.com',
+ >>> 'profile': {'address': '123 Acacia Avenue', 'phone': '01273 100200'}
+ >>> }
+ >>>
+ >>> serializer = UserSerializer(data=data)
+ >>> serializer.save()
+ AssertionError: The `.create()` method does not support nested writable fields by default. Write an explicit `.create()` method for serializer `UserSerializer`, or set `read_only=True` on nested serializer fields.
+
+To use writable nested serialization you'll want to declare a nested field on the serializer class, and write the `create()` and/or `update()` methods explicitly.
+
+ class UserSerializer(serializers.ModelSerializer):
+ profile = ProfileSerializer()
+
+ class Meta:
+ model = User
+ fields = ('username', 'email', 'profile')
+
+ def create(self, validated_data):
+ profile_data = validated_data.pop('profile')
+ user = User.objects.create(**validated_data)
+ Profile.objects.create(user=user, **profile_data)
+ return user
+
+The single-step object creation makes this far simpler and more obvious than the previous `.restore_object()` behavior.
+
+#### Printable serializer representations.
+
+Serializer instances now support a printable representation that allows you to inspect the fields present on the instance.
+
+For instance, given the following example model:
+
+ class LocationRating(models.Model):
+ location = models.CharField(max_length=100)
+ rating = models.IntegerField()
+ created_by = models.ForeignKey(User)
+
+Let's create a simple `ModelSerializer` class corresponding to the `LocationRating` model.
+
+ class LocationRatingSerializer(serializer.ModelSerializer):
+ class Meta:
+ model = LocationRating
+
+We can now inspect the serializer representation in the Django shell, using `python manage.py shell`...
+
+ >>> serializer = LocationRatingSerializer()
+ >>> print(serializer) # Or use `print serializer` in Python 2.x
+ LocationRatingSerializer():
+ id = IntegerField(label='ID', read_only=True)
+ location = CharField(max_length=100)
+ rating = IntegerField()
+ created_by = PrimaryKeyRelatedField(queryset=User.objects.all())
+
+#### The `extra_kwargs` option.
+
+The `write_only_fields` option on `ModelSerializer` has been moved to `PendingDeprecation` and replaced with a more generic `extra_kwargs`.
+
+ class MySerializer(serializer.ModelSerializer):
+ class Meta:
+ model = MyModel
+ fields = ('id', 'email', 'notes', 'is_admin')
+ extra_kwargs = {
+ 'is_admin': {'write_only': True}
+ }
+
+Alternatively, specify the field explicitly on the serializer class:
+
+ class MySerializer(serializer.ModelSerializer):
+ is_admin = serializers.BooleanField(write_only=True)
+
+ class Meta:
+ model = MyModel
+ fields = ('id', 'email', 'notes', 'is_admin')
+
+The `read_only_fields` option remains as a convenient shortcut for the more common case.
+
+#### Changes to `HyperlinkedModelSerializer`.
+
+The `view_name` and `lookup_field` options have been moved to `PendingDeprecation`. They are no longer required, as you can use the `extra_kwargs` argument instead:
+
+ class MySerializer(serializer.HyperlinkedModelSerializer):
+ class Meta:
+ model = MyModel
+ fields = ('url', 'email', 'notes', 'is_admin')
+ extra_kwargs = {
+ 'url': {'lookup_field': 'uuid'}
+ }
+
+Alternatively, specify the field explicitly on the serializer class:
+
+ class MySerializer(serializer.HyperlinkedModelSerializer):
+ url = serializers.HyperlinkedIdentityField(
+ view_name='mymodel-detail',
+ lookup_field='uuid'
+ )
+
+ class Meta:
+ model = MyModel
+ fields = ('url', 'email', 'notes', 'is_admin')
+
+#### Fields for model methods and properties.
+
+With `ModelSerializer` you can now specify field names in the `fields` option that refer to model methods or properties. For example, suppose you have the following model:
+
+ class Invitation(models.Model):
+ created = models.DateTimeField()
+ to_email = models.EmailField()
+ message = models.CharField(max_length=1000)
+
+ def expiry_date(self):
+ return self.created + datetime.timedelta(days=30)
+
+You can include `expiry_date` as a field option on a `ModelSerializer` class.
+
+ class InvitationSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Invitation
+ fields = ('to_email', 'message', 'expiry_date')
+
+These fields will be mapped to `serializers.ReadOnlyField()` instances.
+
+ >>> serializer = InvitationSerializer()
+ >>> print repr(serializer)
+ InvitationSerializer():
+ to_email = EmailField(max_length=75)
+ message = CharField(max_length=1000)
+ expiry_date = ReadOnlyField()
+
+#### The `ListSerializer` class.
+
+The `ListSerializer` class has now been added, and allows you to create base serializer classes for only accepting multiple inputs.
+
+ class MultipleUserSerializer(ListSerializer):
+ child = UserSerializer()
+
+You can also still use the `many=True` argument to serializer classes. It's worth noting that `many=True` argument transparently creates a `ListSerializer` instance, allowing the validation logic for list and non-list data to be cleanly separated in the REST framework codebase.
+
+You will typically want to *continue to use the existing `many=True` flag* rather than declaring `ListSerializer` classes explicitly, but declaring the classes explicitly can be useful if you need to write custom `create` or `update` methods for bulk updates, or provide for other custom behavior.
+
+See also the new `ListField` class, which validates input in the same way, but does not include the serializer interfaces of `.is_valid()`, `.data`, `.save()` and so on.
+
+#### The `BaseSerializer` class.
+
+REST framework now includes a simple `BaseSerializer` class that can be used to easily support alternative serialization and deserialization styles.
+
+This class implements the same basic API as the `Serializer` class:
+
+* `.data` - Returns the outgoing primitive representation.
+* `.is_valid()` - Deserializes and validates incoming data.
+* `.validated_data` - Returns the validated incoming data.
+* `.errors` - Returns an errors during validation.
+* `.save()` - Persists the validated data into an object instance.
+
+There are four methods that can be overridden, depending on what functionality you want the serializer class to support:
+
+* `.to_representation()` - Override this to support serialization, for read operations.
+* `.to_internal_value()` - Override this to support deserialization, for write operations.
+* `.create()` and `.update()` - Override either or both of these to support saving instances.
+
+Because this class provides the same interface as the `Serializer` class, you can use it with the existing generic class based views exactly as you would for a regular `Serializer` or `ModelSerializer`.
+
+The only difference you'll notice when doing so is the `BaseSerializer` classes will not generate HTML forms in the browsable API. This is because the data they return does not include all the field information that would allow each field to be rendered into a suitable HTML input.
+
+##### Read-only `BaseSerializer` classes.
+
+To implement a read-only serializer using the `BaseSerializer` class, we just need to override the `.to_representation()` method. Let's take a look at an example using a simple Django model:
+
+ class HighScore(models.Model):
+ created = models.DateTimeField(auto_now_add=True)
+ player_name = models.CharField(max_length=10)
+ score = models.IntegerField()
+
+It's simple to create a read-only serializer for converting `HighScore` instances into primitive data types.
+
+ class HighScoreSerializer(serializers.BaseSerializer):
+ def to_representation(self, obj):
+ return {
+ 'score': obj.score,
+ 'player_name': obj.player_name
+ }
+
+We can now use this class to serialize single `HighScore` instances:
+
+ @api_view(['GET'])
+ def high_score(request, pk):
+ instance = HighScore.objects.get(pk=pk)
+ serializer = HighScoreSerializer(instance)
+ return Response(serializer.data)
+
+Or use it to serialize multiple instances:
+
+ @api_view(['GET'])
+ def all_high_scores(request):
+ queryset = HighScore.objects.order_by('-score')
+ serializer = HighScoreSerializer(queryset, many=True)
+ return Response(serializer.data)
+
+##### Read-write `BaseSerializer` classes.
+
+To create a read-write serializer we first need to implement a `.to_internal_value()` method. This method returns the validated values that will be used to construct the object instance, and may raise a `ValidationError` if the supplied data is in an incorrect format.
+
+Once you've implemented `.to_internal_value()`, the basic validation API will be available on the serializer, and you will be able to use `.is_valid()`, `.validated_data` and `.errors`.
+
+If you want to also support `.save()` you'll need to also implement either or both of the `.create()` and `.update()` methods.
+
+Here's a complete example of our previous `HighScoreSerializer`, that's been updated to support both read and write operations.
+
+ class HighScoreSerializer(serializers.BaseSerializer):
+ def to_internal_value(self, data):
+ score = data.get('score')
+ player_name = data.get('player_name')
+
+ # Perform the data validation.
+ if not score:
+ raise ValidationError({
+ 'score': 'This field is required.'
+ })
+ if not player_name:
+ raise ValidationError({
+ 'player_name': 'This field is required.'
+ })
+ if len(player_name) > 10:
+ raise ValidationError({
+ 'player_name': 'May not be more than 10 characters.'
+ })
+
+ # Return the validated values. This will be available as
+ # the `.validated_data` property.
+ return {
+ 'score': int(score),
+ 'player_name': player_name
+ }
+
+ def to_representation(self, obj):
+ return {
+ 'score': obj.score,
+ 'player_name': obj.player_name
+ }
+
+ def create(self, validated_data):
+ return HighScore.objects.create(**validated_data)
+
+#### Creating new generic serializers with `BaseSerializer`.
+
+The `BaseSerializer` class is also useful if you want to implement new generic serializer classes for dealing with particular serialization styles, or for integrating with alternative storage backends.
+
+The following class is an example of a generic serializer that can handle coercing arbitrary objects into primitive representations.
+
+ class ObjectSerializer(serializers.BaseSerializer):
+ """
+ A read-only serializer that coerces arbitrary complex objects
+ into primitive representations.
+ """
+ def to_representation(self, obj):
+ for attribute_name in dir(obj):
+ attribute = getattr(obj, attribute_name)
+ if attribute_name('_'):
+ # Ignore private attributes.
+ pass
+ elif hasattr(attribute, '__call__'):
+ # Ignore methods and other callables.
+ pass
+ elif isinstance(attribute, (str, int, bool, float, type(None))):
+ # Primitive types can be passed through unmodified.
+ output[attribute_name] = attribute
+ elif isinstance(attribute, list):
+ # Recursively deal with items in lists.
+ output[attribute_name] = [
+ self.to_representation(item) for item in attribute
+ ]
+ elif isinstance(attribute, dict):
+ # Recursively deal with items in dictionaries.
+ output[attribute_name] = {
+ str(key): self.to_representation(value)
+ for key, value in attribute.items()
+ }
+ else:
+ # Force anything else to its string representation.
+ output[attribute_name] = str(attribute)
+
+---
+
+## Serializer fields
+
+#### The `Field` and `ReadOnly` field classes.
+
+There are some minor tweaks to the field base classes.
+
+Previously we had these two base classes:
+
+* `Field` as the base class for read-only fields. A default implementation was included for serializing data.
+* `WritableField` as the base class for read-write fields.
+
+We now use the following:
+
+* `Field` is the base class for all fields. It does not include any default implementation for either serializing or deserializing data.
+* `ReadOnlyField` is a concrete implementation for read-only fields that simply returns the attribute value without modification.
+
+#### The `required`, `allow_null`, `allow_blank` and `default` arguments.
+
+REST framework now has more explicit and clear control over validating empty values for fields.
+
+Previously the meaning of the `required=False` keyword argument was underspecified. In practice its use meant that a field could either be not included in the input, or it could be included, but be `None` or the empty string.
+
+We now have a better separation, with separate `required`, `allow_null` and `allow_blank` arguments.
+
+The following set of arguments are used to control validation of empty values:
+
+* `required=False`: The value does not need to be present in the input, and will not be passed to `.create()` or `.update()` if it is not seen.
+* `default=<value>`: The value does not need to be present in the input, and a default value will be passed to `.create()` or `.update()` if it is not seen.
+* `allow_null=True`: `None` is a valid input.
+* `allow_blank=True`: `''` is valid input. For `CharField` and subclasses only.
+
+Typically you'll want to use `required=False` if the corresponding model field has a default value, and additionally set either `allow_null=True` or `allow_blank=True` if required.
+
+The `default` argument is also available and always implies that the field is not required to be in the input. It is unnecessary to use the `required` argument when a default is specified, and doing so will result in an error.
+
+#### Coercing output types.
+
+The previous field implementations did not forcibly coerce returned values into the correct type in many cases. For example, an `IntegerField` would return a string output if the attribute value was a string. We now more strictly coerce to the correct return type, leading to more constrained and expected behavior.
+
+#### Removal of `.validate()`.
+
+The `.validate()` method is now removed from field classes. This method was in any case undocumented and not public API. You should instead simply override `to_internal_value()`.
+
+ class UppercaseCharField(serializers.CharField):
+ def to_internal_value(self, data):
+ value = super(UppercaseCharField, self).to_internal_value(data)
+ if value != value.upper():
+ raise serializers.ValidationError('The input should be uppercase only.')
+ return value
+
+Previously validation errors could be raised in either `.to_native()` or `.validate()`, making it non-obvious which should be used. Providing only a single point of API ensures more repetition and reinforcement of the core API.
+
+#### The `ListField` class.
+
+The `ListField` class has now been added. This field validates list input. It takes a `child` keyword argument which is used to specify the field used to validate each item in the list. For example:
+
+ scores = ListField(child=IntegerField(min_value=0, max_value=100))
+
+You can also use a declarative style to create new subclasses of `ListField`, like this:
+
+ class ScoresField(ListField):
+ child = IntegerField(min_value=0, max_value=100)
+
+We can now use the `ScoresField` class inside another serializer:
+
+ scores = ScoresField()
+
+See also the new `ListSerializer` class, which validates input in the same way, but also includes the serializer interfaces of `.is_valid()`, `.data`, `.save()` and so on.
+
+#### The `ChoiceField` class may now accept a flat list.
+
+The `ChoiceField` class may now accept a list of choices in addition to the existing style of using a list of pairs of `(name, display_value)`. The following is now valid:
+
+ color = ChoiceField(choices=['red', 'green', 'blue'])
+
+#### The `MultipleChoiceField` class.
+
+The `MultipleChoiceField` class has been added. This field acts like `ChoiceField`, but returns a set, which may include none, one or many of the valid choices.
+
+#### Changes to the custom field API.
+
+The `from_native(self, value)` and `to_native(self, data)` method names have been replaced with the more obviously named `to_internal_value(self, data)` and `to_representation(self, value)`.
+
+The `field_from_native()` and `field_to_native()` methods are removed. Previously you could use these methods if you wanted to customise the behaviour in a way that did not simply lookup the field value from the object. For example...
+
+ def field_to_native(self, obj, field_name):
+ """A custom read-only field that returns the class name."""
+ return obj.__class__.__name__
+
+Now if you need to access the entire object you'll instead need to override one or both of the following:
+
+* Use `get_attribute` to modify the attribute value passed to `to_representation()`.
+* Use `get_value` to modify the data value passed `to_internal_value()`.
+
+For example:
+
+ def get_attribute(self, obj):
+ # Pass the entire object through to `to_representation()`,
+ # instead of the standard attribute lookup.
+ return obj
+
+ def to_representation(self, value):
+ return value.__class__.__name__
+
+#### Explicit `queryset` required on relational fields.
+
+Previously relational fields that were explicitly declared on a serializer class could omit the queryset argument if (and only if) they were declared on a `ModelSerializer`.
+
+This code *would be valid* in `2.4.3`:
+
+ class AccountSerializer(serializers.ModelSerializer):
+ organizations = serializers.SlugRelatedField(slug_field='name')
+
+ class Meta:
+ model = Account
+
+However this code *would not be valid* in `3.0`:
+
+ # Missing `queryset`
+ class AccountSerializer(serializers.Serializer):
+ organizations = serializers.SlugRelatedField(slug_field='name')
+
+ def restore_object(self, attrs, instance=None):
+ # ...
+
+The queryset argument is now always required for writable relational fields.
+This removes some magic and makes it easier and more obvious to move between implicit `ModelSerializer` classes and explicit `Serializer` classes.
+
+ class AccountSerializer(serializers.ModelSerializer):
+ organizations = serializers.SlugRelatedField(
+ slug_field='name',
+ queryset=Organization.objects.all()
+ )
+
+ class Meta:
+ model = Account
+
+The `queryset` argument is only ever required for writable fields, and is not required or valid for fields with `read_only=True`.
+
+#### Optional argument to `SerializerMethodField`.
+
+The argument to `SerializerMethodField` is now optional, and defaults to `get_<field_name>`. For example the following is valid:
+
+ class AccountSerializer(serializers.Serializer):
+ # `method_name='get_billing_details'` by default.
+ billing_details = serializers.SerializerMethodField()
+
+ def get_billing_details(self, account):
+ return calculate_billing(account)
+
+In order to ensure a consistent code style an assertion error will be raised if you include a redundant method name argument that matches the default method name. For example, the following code *will raise an error*:
+
+ billing_details = serializers.SerializerMethodField('get_billing_details')
+
+#### Enforcing consistent `source` usage.
+
+I've see several codebases that unnecessarily include the `source` argument, setting it to the same value as the field name. This usage is redundant and confusing, making it less obvious that `source` is usually not required.
+
+The following usage will *now raise an error*:
+
+ email = serializers.EmailField(source='email')
+
+#### The `UniqueValidator` and `UniqueTogetherValidator` classes.
+
+REST framework now provides new validators that allow you to ensure field uniqueness, while still using a completely explicit `Serializer` class instead of using `ModelSerializer`.
+
+The `UniqueValidator` should be applied to a serializer field, and takes a single `queryset` argument.
+
+ from rest_framework import serializers
+ from rest_framework.validators import UniqueValidator
+
+ class OrganizationSerializer(serializers.Serializer):
+ url = serializers.HyperlinkedIdentityField(view_name='organization_detail')
+ created = serializers.DateTimeField(read_only=True)
+ name = serializers.CharField(
+ max_length=100,
+ validators=UniqueValidator(queryset=Organization.objects.all())
+ )
+
+The `UniqueTogetherValidator` should be applied to a serializer, and takes a `queryset` argument and a `fields` argument which should be a list or tuple of field names.
+
+ class RaceResultSerializer(serializers.Serializer):
+ category = serializers.ChoiceField(['5k', '10k'])
+ position = serializers.IntegerField()
+ name = serializers.CharField(max_length=100)
+
+ class Meta:
+ validators = [UniqueTogetherValidator(
+ queryset=RaceResult.objects.all(),
+ fields=('category', 'position')
+ )]
+
+#### The `UniqueForDateValidator` classes.
+
+REST framework also now includes explicit validator classes for validating the `unique_for_date`, `unique_for_month`, and `unique_for_year` model field constraints. These are used internally instead of calling into `Model.full_clean()`.
+
+These classes are documented in the [Validators](../api-guide/validators.md) section of the documentation.
+
+---
+
+## Generic views
+
+#### Simplification of view logic.
+
+The view logic for the default method handlers has been significantly simplified, due to the new serializers API.
+
+#### Changes to pre/post save hooks.
+
+The `pre_save` and `post_save` hooks no longer exist, but are replaced with `perform_create(self, serializer)` and `perform_update(self, serializer)`.
+
+These methods should save the object instance by calling `serializer.save()`, adding in any additional arguments as required. They may also perform any custom pre-save or post-save behavior.
+
+For example:
+
+ def perform_create(self, serializer):
+ # Include the owner attribute directly, rather than from request data.
+ instance = serializer.save(owner=self.request.user)
+ # Perform a custom post-save action.
+ send_email(instance.to_email, instance.message)
+
+The `pre_delete` and `post_delete` hooks no longer exist, and are replaced with `.perform_destroy(self, instance)`, which should delete the instance and perform any custom actions.
+
+ def perform_destroy(self, instance):
+ # Perform a custom pre-delete action.
+ send_deletion_alert(user=instance.created_by, deleted=instance)
+ # Delete the object instance.
+ instance.delete()
+
+#### Removal of view attributes.
+
+The `.object` and `.object_list` attributes are no longer set on the view instance. Treating views as mutable object instances that store state during the processing of the view tends to be poor design, and can lead to obscure flow logic.
+
+I would personally recommend that developers treat view instances as immutable objects in their application code.
+
+#### PUT as create.
+
+Allowing `PUT` as create operations is problematic, as it necessarily exposes information about the existence or non-existence of objects. It's also not obvious that transparently allowing re-creating of previously deleted instances is necessarily a better default behavior than simply returning `404` responses.
+
+Both styles "`PUT` as 404" and "`PUT` as create" can be valid in different circumstances, but we've now opted for the 404 behavior as the default, due to it being simpler and more obvious.
+
+If you need to restore the previous behavior you may want to include [this `AllowPUTAsCreateMixin` class](https://gist.github.com/tomchristie/a2ace4577eff2c603b1b) as a mixin to your views.
+
+#### Customizing error responses.
+
+The generic views now raise `ValidationFailed` exception for invalid data. This exception is then dealt with by the exception handler, rather than the view returning a `400 Bad Request` response directly.
+
+This change means that you can now easily customize the style of error responses across your entire API, without having to modify any of the generic views.
+
+---
+
+## The metadata API
+
+Behavior for dealing with `OPTIONS` requests was previously built directly into the class based views. This has now been properly separated out into a Metadata API that allows the same pluggable style as other API policies in REST framework.
+
+This makes it far easier to use a different style for `OPTIONS` responses throughout your API, and makes it possible to create third-party metadata policies.
+
+---
+
+## Serializers as HTML forms
+
+REST framework 3.0 includes templated HTML form rendering for serializers.
+
+This API should not yet be considered finalized, and will only be promoted to public API for the 3.1 release.
+
+Significant changes that you do need to be aware of include:
+
+* Nested HTML forms are now supported, for example, a `UserSerializer` with a nested `ProfileSerializer` will now render a nested `fieldset` when used in the browsable API.
+* Nested lists of HTML forms are not yet supported, but are planned for 3.1.
+* Because we now use templated HTML form generation, **the `widget` option is no longer available for serializer fields**. You can instead control the template that is used for a given field, by using the `style` dictionary.
+
+#### The `style` keyword argument for serializer fields.
+
+The `style` keyword argument can be used to pass through additional information from a serializer field, to the renderer class. In particular, the `HTMLFormRenderer` uses the `base_template` key to determine which template to render the field with.
+
+For example, to use a `textarea` control instead of the default `input` control, you would use the following…
+
+ additional_notes = serializers.CharField(
+ style={'base_template': 'textarea.html'}
+ )
+
+Similarly, to use a radio button control instead of the default `select` control, you would use the following…
+
+ color_channel = serializers.ChoiceField(
+ choices=['red', 'blue', 'green'],
+ style={'base_template': 'radio.html'}
+ )
+
+This API should be considered provisional, and there may be minor alterations with the incoming 3.1 release.
+
+---
+
+## API style
+
+There are some improvements in the default style we use in our API responses.
+
+#### Unicode JSON by default.
+
+Unicode JSON is now the default. The `UnicodeJSONRenderer` class no longer exists, and the `UNICODE_JSON` setting has been added. To revert this behavior use the new setting:
+
+ REST_FRAMEWORK = {
+ 'UNICODE_JSON': False
+ }
+
+#### Compact JSON by default.
+
+We now output compact JSON in responses by default. For example, we return:
+
+ {"email":"amy@example.com","is_admin":true}
+
+Instead of the following:
+
+ {"email": "amy@example.com", "is_admin": true}
+
+The `COMPACT_JSON` setting has been added, and can be used to revert this behavior if needed:
+
+ REST_FRAMEWORK = {
+ 'COMPACT_JSON': False
+ }
+
+#### File fields as URLs
+
+The `FileField` and `ImageField` classes are now represented as URLs by default. You should ensure you set Django's [standard `MEDIA_URL` setting](https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-MEDIA_URL) appropriately, and ensure your application [serves the uploaded files](https://docs.djangoproject.com/en/dev/howto/static-files/#serving-uploaded-files-in-development).
+
+You can revert this behavior, and display filenames in the representation by using the `UPLOADED_FILES_USE_URL` settings key:
+
+ REST_FRAMEWORK = {
+ 'UPLOADED_FILES_USE_URL': False
+ }
+
+You can also modify serializer fields individually, using the `use_url` argument:
+
+ uploaded_file = serializers.FileField(use_url=False)
+
+Also note that you should pass the `request` object to the serializer as context when instantiating it, so that a fully qualified URL can be returned. Returned URLs will then be of the form `https://example.com/url_path/filename.txt`. For example:
+
+ context = {'request': request}
+ serializer = ExampleSerializer(instance, context=context)
+ return Response(serializer.data)
+
+If the request is omitted from the context, the returned URLs will be of the form `/url_path/filename.txt`.
+
+#### Throttle headers using `Retry-After`.
+
+The custom `X-Throttle-Wait-Second` header has now been dropped in favor of the standard `Retry-After` header. You can revert this behavior if needed by writing a custom exception handler for your application.
+
+#### Date and time objects as ISO-8859-1 strings in serializer data.
+
+Date and Time objects are now coerced to strings by default in the serializer output. Previously they were returned as `Date`, `Time` and `DateTime` objects, and later coerced to strings by the renderer.
+
+You can modify this behavior globally by settings the existing `DATE_FORMAT`, `DATETIME_FORMAT` and `TIME_FORMAT` settings keys. Setting these values to `None` instead of their default value of `'iso-8859-1'` will result in native objects being returned in serializer data.
+
+ REST_FRAMEWORK = {
+ # Return native `Date` and `Time` objects in `serializer.data`
+ 'DATETIME_FORMAT': None
+ 'DATE_FORMAT': None
+ 'TIME_FORMAT': None
+ }
+
+You can also modify serializer fields individually, using the `date_format`, `time_format` and `datetime_format` arguments:
+
+ # Return `DateTime` instances in `serializer.data`, not strings.
+ created = serializers.DateTimeField(format=None)
+
+#### Decimals as strings in serializer data.
+
+Decimals are now coerced to strings by default in the serializer output. Previously they were returned as `Decimal` objects, and later coerced to strings by the renderer.
+
+You can modify this behavior globally by using the `COERCE_DECIMAL_TO_STRING` settings key.
+
+ REST_FRAMEWORK = {
+ 'COERCE_DECIMAL_TO_STRING': False
+ }
+
+Or modify it on an individual serializer field, using the `coerce_to_string` keyword argument.
+
+ # Return `Decimal` instances in `serializer.data`, not strings.
+ amount = serializers.DecimalField(
+ max_digits=10,
+ decimal_places=2,
+ coerce_to_string=False
+ )
+
+The default JSON renderer will return float objects for un-coerced `Decimal` instances. This allows you to easily switch between string or float representations for decimals depending on your API design needs.
+
+---
+
+## Miscellaneous notes
+
+* The serializer `ChoiceField` does not currently display nested choices, as was the case in 2.4. This will be address as part of 3.1.
+* Due to the new templated form rendering, the 'widget' option is no longer valid. This means there's no easy way of using third party "autocomplete" widgets for rendering select inputs that contain a large number of choices. You'll either need to use a regular select or a plain text input. We may consider addressing this in 3.1 or 3.2 if there's sufficient demand.
+* Some of the default validation error messages were rewritten and might no longer be pre-translated. You can still [create language files with Django][django-localization] if you wish to localize them.
+* `APIException` subclasses could previously take could previously take any arbitrary type in the `detail` argument. These exceptions now use translatable text strings, and as a result call `force_text` on the `detail` argument, which *must be a string*. If you need complex arguments to an `APIException` class, you should subclass it and override the `__init__()` method. Typically you'll instead want to use a custom exception handler to provide for non-standard error responses.
+
+---
+
+## What's coming next
+
+3.0 is an incremental release, and there are several upcoming features that will build on the baseline improvements that it makes.
+
+The 3.1 release is planned to address improvements in the following components:
+
+* Public API for using serializers as HTML forms.
+* Request parsing, mediatypes & the implementation of the browsable API.
+* Introduction of a new pagination API.
+* Better support for API versioning.
+
+The 3.2 release is planned to introduce an alternative admin-style interface to the browsable API.
+
+You can follow development on the GitHub site, where we use [milestones to indicate planning timescales](https://github.com/tomchristie/django-rest-framework/milestones).
+
+[kickstarter]: http://kickstarter.com/projects/tomchristie/django-rest-framework-3
+[sponsors]: http://www.django-rest-framework.org/topics/kickstarter-announcement/#sponsors
+[mixins.py]: https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/mixins.py
+[django-localization]: https://docs.djangoproject.com/en/dev/topics/i18n/translation/#localization-how-to-create-language-files
diff --git a/docs/topics/3.1-announcement.md b/docs/topics/3.1-announcement.md
new file mode 100644
index 00000000..80d4007e
--- /dev/null
+++ b/docs/topics/3.1-announcement.md
@@ -0,0 +1,209 @@
+# Django REST framework 3.1
+
+The 3.1 release is an intermediate step in the Kickstarter project releases, and includes a range of new functionality.
+
+Some highlights include:
+
+* A super-smart cursor pagination scheme.
+* An improved pagination API, supporting header or in-body pagination styles.
+* Pagination controls rendering in the browsable API.
+* Better support for API versioning.
+* Built-in internationalization support.
+* Support for Django 1.8's `HStoreField` and `ArrayField`.
+
+---
+
+## Pagination
+
+The pagination API has been improved, making it both easier to use, and more powerful.
+
+A guide to the headline features follows. For full details, see [the pagination documentation][pagination].
+
+Note that as a result of this work a number of settings keys and generic view attributes are now moved to pending deprecation. Controlling pagination styles is now largely handled by overriding a pagination class and modifying its configuration attributes.
+
+* The `PAGINATE_BY` settings key will continue to work but is now pending deprecation. The more obviously named `PAGE_SIZE` settings key should now be used instead.
+* The `PAGINATE_BY_PARAM`, `MAX_PAGINATE_BY` settings keys will continue to work but are now pending deprecation, in favor of setting configuration attributes on the configured pagination class.
+* The `paginate_by`, `page_query_param`, `paginate_by_param` and `max_paginate_by` generic view attributes will continue to work but are now pending deprecation, in favor of setting configuration attributes on the configured pagination class.
+* The `pagination_serializer_class` view attribute and `DEFAULT_PAGINATION_SERIALIZER_CLASS` settings key **are no longer valid**. The pagination API does not use serializers to determine the output format, and you'll need to instead override the `get_paginated_response` method on a pagination class in order to specify how the output format is controlled.
+
+#### New pagination schemes.
+
+Until now, there has only been a single built-in pagination style in REST framework. We now have page, limit/offset and cursor based schemes included by default.
+
+The cursor based pagination scheme is particularly smart, and is a better approach for clients iterating through large or frequently changing result sets. The scheme supports paging against non-unique indexes, by using both cursor and limit/offset information. It also allows for both forward and reverse cursor pagination. Much credit goes to David Cramer for [this blog post](http://cramer.io/2011/03/08/building-cursors-for-the-disqus-api/) on the subject.
+
+#### Pagination controls in the browsable API.
+
+Paginated results now include controls that render directly in the browsable API. If you're using the page or limit/offset style, then you'll see a page based control displayed in the browsable API:
+
+![page number based pagination](../img/pages-pagination.png )
+
+The cursor based pagination renders a more simple style of control:
+
+![cursor based pagination](../img/cursor-pagination.png )
+
+#### Support for header-based pagination.
+
+The pagination API was previously only able to alter the pagination style in the body of the response. The API now supports being able to write pagination information in response headers, making it possible to use pagination schemes that use the `Link` or `Content-Range` headers.
+
+For more information, see the [custom pagination styles](../api-guide/pagination/#custom-pagination-styles) documentation.
+
+---
+
+## Versioning
+
+We've made it [easier to build versioned APIs][versioning]. Built-in schemes for versioning include both URL based and Accept header based variations.
+
+When using a URL based scheme, hyperlinked serializers will resolve relationships to the same API version as used on the incoming request.
+
+For example, when using `NamespaceVersioning`, and the following hyperlinked serializer:
+
+ class AccountsSerializer(serializer.HyperlinkedModelSerializer):
+ class Meta:
+ model = Accounts
+ fields = ('account_name', 'users')
+
+The output representation would match the version used on the incoming request. Like so:
+
+ GET http://example.org/v2/accounts/10 # Version 'v2'
+
+ {
+ "account_name": "europa",
+ "users": [
+ "http://example.org/v2/users/12", # Version 'v2'
+ "http://example.org/v2/users/54",
+ "http://example.org/v2/users/87"
+ ]
+ }
+
+---
+
+## Internationalization
+
+REST framework now includes a built-in set of translations, and [supports internationalized error responses][internationalization]. This allows you to either change the default language, or to allow clients to specify the language via the `Accept-Language` header.
+
+You can change the default language by using the standard Django `LANGUAGE_CODE` setting:
+
+ LANGUAGE_CODE = "es-es"
+
+You can turn on per-request language requests by adding `LocalMiddleware` to your `MIDDLEWARE_CLASSES` setting:
+
+ MIDDLEWARE_CLASSES = [
+ ...
+ 'django.middleware.locale.LocaleMiddleware'
+ ]
+
+When per-request internationalization is enabled, client requests will respect the `Accept-Language` header where possible. For example, let's make a request for an unsupported media type:
+
+**Request**
+
+ GET /api/users HTTP/1.1
+ Accept: application/xml
+ Accept-Language: es-es
+ Host: example.org
+
+**Response**
+
+ HTTP/1.0 406 NOT ACCEPTABLE
+
+ {
+ "detail": "No se ha podido satisfacer la solicitud de cabecera de Accept."
+ }
+
+Note that the structure of the error responses is still the same. We still have a `details` key in the response. If needed you can modify this behavior too, by using a [custom exception handler][custom-exception-handler].
+
+We include built-in translations both for standard exception cases, and for serializer validation errors.
+
+The full list of supported languages can be found on our [Transifex project page](https://www.transifex.com/projects/p/django-rest-framework/).
+
+If you only wish to support a subset of the supported languages, use Django's standard `LANGUAGES` setting:
+
+ LANGUAGES = [
+ ('de', _('German')),
+ ('en', _('English')),
+ ]
+
+For more details, see the [internationalization documentation](internationalization.md).
+
+Many thanks to [Craig Blaszczyk](https://github.com/jakul) for helping push this through.
+
+---
+
+## New field types
+
+Django 1.8's new `ArrayField`, `HStoreField` and `UUIDField` are now all fully supported.
+
+This work also means that we now have both `serializers.DictField()`, and `serializers.ListField()` types, allowing you to express and validate a wider set of representations.
+
+If you're building a new 1.8 project, then you should probably consider using `UUIDField` as the primary keys for all your models. This style will work automatically with hyperlinked serializers, returning URLs in the following style:
+
+ http://example.org/api/purchases/9b1a433f-e90d-4948-848b-300fdc26365d
+
+---
+
+## ModelSerializer API
+
+The serializer redesign in 3.0 did not include any public API for modifying how ModelSerializer classes automatically generate a set of fields from a given mode class. We've now re-introduced an API for this, allowing you to create new ModelSerializer base classes that behave differently, such as using a different default style for relationships.
+
+For more information, see the documentation on [customizing field mappings][customizing-field-mappings] for ModelSerializer classes.
+
+---
+
+## Moving packages out of core
+
+We've now moved a number of packages out of the core of REST framework, and into separately installable packages. If you're currently using these you don't need to worry, you simply need to `pip install` the new packages, and change any import paths.
+
+We're making this change in order to help distribute the maintainance workload, and keep better focus of the core essentials of the framework.
+
+The change also means we can be more flexible with which external packages we recommend. For example, the excellently maintained [Django OAuth toolkit](https://github.com/evonove/django-oauth-toolkit) has now been promoted as our recommended option for integrating OAuth support.
+
+The following packages are now moved out of core and should be separately installed:
+
+* OAuth - [djangorestframework-oauth](http://jpadilla.github.io/django-rest-framework-oauth/)
+* XML - [djangorestframework-xml](http://jpadilla.github.io/django-rest-framework-xml)
+* YAML - [djangorestframework-yaml](http://jpadilla.github.io/django-rest-framework-yaml)
+* JSONP - [djangorestframework-jsonp](http://jpadilla.github.io/django-rest-framework-jsonp)
+
+It's worth reiterating that this change in policy shouldn't mean any work in your codebase other than adding a new requirement and modifying some import paths. For example to install XML rendering, you would now do:
+
+ pip install djangorestframework-xml
+
+And modify your settings, like so:
+
+ REST_FRAMEWORK = {
+ 'DEFAULT_RENDERER_CLASSES': [
+ 'rest_framework.renderers.JSONRenderer',
+ 'rest_framework.renderers.BrowsableAPIRenderer',
+ 'rest_framework_xml.renderers.XMLRenderer'
+ ]
+ }
+
+Thanks go to the latest member of our maintenance team, [José Padilla](https://github.com/jpadilla/), for handling this work and taking on ownership of these packages.
+
+---
+
+## Deprecations
+
+The `request.DATA`, `request.FILES` and `request.QUERY_PARAMS` attributes move from pending deprecation, to deprecated. Use `request.data` and `request.query_params` instead, as discussed in the 3.0 release notes.
+
+The ModelSerializer Meta options for `write_only_fields`, `view_name` and `lookup_field` are also moved from pending deprecation, to deprecated. Use `extra_kwargs` instead, as discussed in the 3.0 release notes.
+
+All these attributes and options will still work in 3.1, but their usage will raise a warning. They will be fully removed in 3.2.
+
+---
+
+## What's next?
+
+The next focus will be on HTML renderings of API output and will include:
+
+* HTML form rendering of serializers.
+* Filtering controls built-in to the browsable API.
+* An alternative admin-style interface.
+
+This will either be made as a single 3.2 release, or split across two separate releases, with the HTML forms and filter controls coming in 3.2, and the admin-style interface coming in a 3.3 release.
+
+[custom-exception-handler]: ../api-guide/exceptions.md#custom-exception-handling
+[pagination]: ../api-guide/pagination.md
+[versioning]: ../api-guide/versioning.md
+[internationalization]: internationalization.md
+[customizing-field-mappings]: ../api-guide/serializers.md#customizing-field-mappings
diff --git a/docs/topics/ajax-csrf-cors.md b/docs/topics/ajax-csrf-cors.md
index 0555b84d..ad88810d 100644
--- a/docs/topics/ajax-csrf-cors.md
+++ b/docs/topics/ajax-csrf-cors.md
@@ -6,11 +6,11 @@
## Javascript clients
-If your building a javascript client to interface with your Web API, you'll need to consider if the client can use the same authentication policy that is used by the rest of the website, and also determine if you need to use CSRF tokens or CORS headers.
+If you’re building a JavaScript client to interface with your Web API, you'll need to consider if the client can use the same authentication policy that is used by the rest of the website, and also determine if you need to use CSRF tokens or CORS headers.
AJAX requests that are made within the same context as the API they are interacting with will typically use `SessionAuthentication`. This ensures that once a user has logged in, any AJAX requests made can be authenticated using the same session-based authentication that is used for the rest of the website.
-AJAX requests that are made on a different site from the API they are communicating with will typically need to use a non-session-based authentication scheme, such as `TokenAuthentication`.
+AJAX requests that are made on a different site from the API they are communicating with will typically need to use a non-session-based authentication scheme, such as `TokenAuthentication`.
## CSRF protection
@@ -19,7 +19,7 @@ AJAX requests that are made on a different site from the API they are communicat
To guard against these type of attacks, you need to do two things:
1. Ensure that the 'safe' HTTP operations, such as `GET`, `HEAD` and `OPTIONS` cannot be used to alter any server-side state.
-2. Ensure that any 'unsafe' HTTP operations, such as `POST`, `PUT`, `PATCH` and `DELETE`, always require a valid CSRF token.
+2. Ensure that any 'unsafe' HTTP operations, such as `POST`, `PUT`, `PATCH` and `DELETE`, always require a valid CSRF token.
If you're using `SessionAuthentication` you'll need to include valid CSRF tokens for any `POST`, `PUT`, `PATCH` or `DELETE` operations.
@@ -35,7 +35,7 @@ The best way to deal with CORS in REST framework is to add the required response
[cite]: http://www.codinghorror.com/blog/2008/10/preventing-csrf-and-xsrf-attacks.html
[csrf]: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
-[csrf-ajax]: https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax
+[csrf-ajax]: https://docs.djangoproject.com/en/dev/ref/csrf/#ajax
[cors]: http://www.w3.org/TR/cors/
[ottoyiu]: https://github.com/ottoyiu/
[django-cors-headers]: https://github.com/ottoyiu/django-cors-headers/
diff --git a/docs/topics/browsable-api.md b/docs/topics/browsable-api.md
index 2ae8cadb..2879db74 100644
--- a/docs/topics/browsable-api.md
+++ b/docs/topics/browsable-api.md
@@ -69,6 +69,7 @@ For more specific CSS tweaks than simply overriding the default bootstrap theme
All of the blocks available in the browsable API base template that can be used in your `api.html`.
+* `body` - The entire html `<body>`.
* `bodyclass` - Class attribute for the `<body>` tag, empty by default.
* `bootstrap_theme` - CSS for the Bootstrap theme.
* `bootstrap_navbar_variant` - CSS class for the navbar.
@@ -90,7 +91,7 @@ The browsable API makes use of the Bootstrap tooltips component. Any element wi
### Login Template
-To add branding and customize the look-and-feel of the login template, create a template called `login.html` and add it to your project, eg: `templates/rest_framework/login.html`. The template should extend from `rest_framework/base_login.html`.
+To add branding and customize the look-and-feel of the login template, create a template called `login.html` and add it to your project, eg: `templates/rest_framework/login.html`. The template should extend from `rest_framework/login_base.html`.
You can add your site name or branding by including the branding block:
@@ -115,6 +116,7 @@ The context that's available to the template:
* `name` : The name of the resource
* `post_form` : A form instance for use by the POST form (if allowed)
* `put_form` : A form instance for use by the PUT form (if allowed)
+* `display_edit_forms` : A boolean indicating whether or not POST, PUT and PATCH forms will be displayed
* `request` : The request object
* `response` : The response object
* `version` : The version of Django REST Framework
@@ -122,38 +124,30 @@ The context that's available to the template:
* `FORMAT_PARAM` : The view can accept a format override
* `METHOD_PARAM` : The view can accept a method override
+You can override the `BrowsableAPIRenderer.get_context()` method to customise the context that gets passed to the template.
+
#### Not using base.html
For more advanced customization, such as not having a Bootstrap basis or tighter integration with the rest of your site, you can simply choose not to have `api.html` extend `base.html`. Then the page content and capabilities are entirely up to you.
-#### Autocompletion
-
-When a `ChoiceField` has too many items, rendering the widget containing all the options can become very slow, and cause the browsable API rendering to perform poorly. One solution is to replace the selector by an autocomplete widget, that only loads and renders a subset of the available options as needed.
-
-There are [a variety of packages for autocomplete widgets][autocomplete-packages], such as [django-autocomplete-light][django-autocomplete-light]. To setup `django-autocomplete-light`, follow the [installation documentation][django-autocomplete-light-install], add the the following to the `api.html` template:
+#### Handling `ChoiceField` with large numbers of items.
- {% block script %}
- {{ block.super }}
- {% include 'autocomplete_light/static.html' %}
- {% endblock %}
-
-You can now add the `autocomplete_light.ChoiceWidget` widget to the serializer field.
+When a relationship or `ChoiceField` has too many items, rendering the widget containing all the options can become very slow, and cause the browsable API rendering to perform poorly.
- import autocomplete_light
+The simplest option in this case is to replace the select input with a standard text input. For example:
- class BookSerializer(serializers.ModelSerializer):
- author = serializers.ChoiceField(
- widget=autocomplete_light.ChoiceWidget('AuthorAutocomplete')
- )
+ author = serializers.HyperlinkedRelatedField(
+ queryset=User.objects.all(),
+ style={'base_template': 'input.html'}
+ )
- class Meta:
- model = Book
+#### Autocomplete
----
+An alternative, but more complex option would be to replace the input with an autocomplete widget, that only loads and renders a subset of the available options as needed. If you need to do this you'll need to do some work to build a custom autocomplete HTML template yourself.
-![Autocomplete][autocomplete-image]
+There are [a variety of packages for autocomplete widgets][autocomplete-packages], such as [django-autocomplete-light][django-autocomplete-light], that you may want to refer to. Note that you will not be able to simply include these components as standard widgets, but will need to write the HTML template explicitly. This is because REST framework 3.0 no longer supports the `widget` keyword argument since it now uses templated HTML generation.
-*Screenshot of the autocomplete-light widget*
+Better support for autocomplete inputs is planned in future versions.
---
@@ -164,11 +158,10 @@ You can now add the `autocomplete_light.ChoiceWidget` widget to the serializer f
[bootstrap]: http://getbootstrap.com
[cerulean]: ../img/cerulean.png
[slate]: ../img/slate.png
-[bcustomize]: http://twitter.github.com/bootstrap/customize.html#variables
+[bcustomize]: http://getbootstrap.com/2.3.2/customize.html
[bswatch]: http://bootswatch.com/
-[bcomponents]: http://twitter.github.com/bootstrap/components.html
-[bcomponentsnav]: http://twitter.github.com/bootstrap/components.html#navbar
+[bcomponents]: http://getbootstrap.com/2.3.2/components.html
+[bcomponentsnav]: http://getbootstrap.com/2.3.2/components.html#navbar
[autocomplete-packages]: https://www.djangopackages.com/grids/g/auto-complete/
[django-autocomplete-light]: https://github.com/yourlabs/django-autocomplete-light
[django-autocomplete-light-install]: http://django-autocomplete-light.readthedocs.org/en/latest/#install
-[autocomplete-image]: ../img/autocomplete.png
diff --git a/docs/topics/contributing.md b/docs/topics/contributing.md
index 123e4a8a..c9626ebf 100644
--- a/docs/topics/contributing.md
+++ b/docs/topics/contributing.md
@@ -6,68 +6,143 @@
There are many ways you can contribute to Django REST framework. We'd like it to be a community-led project, so please get involved and help shape the future of the project.
-# Community
+## Community
-If you use and enjoy REST framework please consider [staring the project on GitHub][github], and [upvoting it on Django packages][django-packages]. Doing so helps potential new users see that the project is well used, and help us continue to attract new users.
+The most important thing you can do to help push the REST framework project forward is to be actively involved wherever possible. Code contributions are often overvalued as being the primary way to get involved in a project, we don't believe that needs to be the case.
-You might also consider writing a blog post on your experience with using REST framework, writing a tutorial about using the project with a particular javascript framework, or simply sharing the love on Twitter.
+If you use REST framework, we'd love you to be vocal about your experiences with it - you might consider writing a blog post about using REST framework, or publishing a tutorial about building a project with a particular JavaScript framework. Experiences from beginners can be particularly helpful because you'll be in the best position to assess which bits of REST framework are more difficult to understand and work with.
-Other really great ways you can help move the community forward include helping answer questions on the [discussion group][google-group], or setting up an [email alert on StackOverflow][so-filter] so that you get notified of any new questions with the `django-rest-framework` tag.
+Other really great ways you can help move the community forward include helping to answer questions on the [discussion group][google-group], or setting up an [email alert on StackOverflow][so-filter] so that you get notified of any new questions with the `django-rest-framework` tag.
-When answering questions make sure to help future contributors find their way around by hyperlinking wherever possible to related threads and tickets, and include backlinks from those items if relevant.
+When answering questions make sure to help future contributors find their way around by hyperlinking wherever possible to related threads and tickets, and include backlinks from those items if relevant.
+
+## Code of conduct
+
+Please keep the tone polite & professional. For some users a discussion on the REST framework mailing list or ticket tracker may be their first engagement with the open source community. First impressions count, so let's try to make everyone feel welcome.
+
+Be mindful in the language you choose. As an example, in an environment that is heavily male-dominated, posts that start 'Hey guys,' can come across as unintentionally exclusive. It's just as easy, and more inclusive to use gender neutral language in those situations.
+
+The [Django code of conduct][code-of-conduct] gives a fuller set of guidelines for participating in community forums.
# Issues
-It's really helpful if you make sure you address issues to the correct channel. Usage questions should be directed to the [discussion group][google-group]. Feature requests, bug reports and other issues should be raised on the GitHub [issue tracker][issues].
+It's really helpful if you can make sure to address issues on the correct channel. Usage questions should be directed to the [discussion group][google-group]. Feature requests, bug reports and other issues should be raised on the GitHub [issue tracker][issues].
Some tips on good issue reporting:
* When describing issues try to phrase your ticket in terms of the *behavior* you think needs changing rather than the *code* you think need changing.
* Search the issue list first for related items, and make sure you're running the latest version of REST framework before reporting an issue.
* If reporting a bug, then try to include a pull request with a failing test case. This will help us quickly identify if there is a valid issue, and make sure that it gets fixed more quickly if there is one.
+* Feature requests will often be closed with a recommendation that they be implemented outside of the core REST framework library. Keeping new feature requests implemented as third party libraries allows us to keep down the maintenance overhead of REST framework, so that the focus can be on continued stability, bugfixes, and great documentation.
+* Closing an issue doesn't necessarily mean the end of a discussion. If you believe your issue has been closed incorrectly, explain why and we'll consider if it needs to be reopened.
+## Triaging issues
+Getting involved in triaging incoming issues is a good way to start contributing. Every single ticket that comes into the ticket tracker needs to be reviewed in order to determine what the next steps should be. Anyone can help out with this, you just need to be willing to
-* TODO: Triage
+* Read through the ticket - does it make sense, is it missing any context that would help explain it better?
+* Is the ticket reported in the correct place, would it be better suited as a discussion on the discussion group?
+* If the ticket is a bug report, can you reproduce it? Are you able to write a failing test case that demonstrates the issue and that can be submitted as a pull request?
+* If the ticket is a feature request, do you agree with it, and could the feature request instead be implemented as a third party package?
+* If a ticket hasn't had much activity and it addresses something you need, then comment on the ticket and try to find out what's needed to get it moving again.
# Development
+To start developing on Django REST framework, clone the repo:
+
+ git clone git@github.com:tomchristie/django-rest-framework.git
+
+Changes should broadly follow the [PEP 8][pep-8] style conventions, and we recommend you set up your editor to automatically indicate non-conforming styles.
+
+## Testing
+
+To run the tests, clone the repository, and then:
+
+ # Setup the virtual environment
+ virtualenv env
+ source env/bin/activate
+ pip install -r requirements.txt
+
+ # Run the tests
+ ./runtests.py
+
+### Test options
+
+Run using a more concise output style.
+
+ ./runtests.py -q
+
+Run the tests using a more concise output style, no coverage, no flake8.
+
+ ./runtests.py --fast
+
+Don't run the flake8 code linting.
+
+ ./runtests.py --nolint
+
+Only run the flake8 code linting, don't run the tests.
+
+ ./runtests.py --lintonly
+
+Run the tests for a given test case.
-* git clone & PYTHONPATH
-* Pep8
-* Recommend editor that runs pep8
+ ./runtests.py MyTestCase
-### Pull requests
+Run the tests for a given test method.
-* Make pull requests early
-* Describe branching
+ ./runtests.py MyTestCase.test_this_method
-### Managing compatibility issues
+Shorter form to run the tests for a given test method.
-* Describe compat module
+ ./runtests.py test_this_method
-# Testing
+Note: The test case and test method matching is fuzzy and will sometimes run other tests that contain a partial string match to the given command line input.
-* Running the tests
-* tox
+### Running against multiple environments
+
+You can also use the excellent [tox][tox] testing tool to run the tests against all supported versions of Python and Django. Install `tox` globally, and then simply run:
+
+ tox
+
+## Pull requests
+
+It's a good idea to make pull requests early on. A pull request represents the start of a discussion, and doesn't necessarily need to be the final, finished submission.
+
+It's also always best to make a new branch before starting work on a pull request. This means that you'll be able to later switch back to working on another separate issue without interfering with an ongoing pull requests.
+
+It's also useful to remember that if you have an outstanding pull request then pushing new commits to your GitHub repo will also automatically update the pull requests.
+
+GitHub's documentation for working on pull requests is [available here][pull-requests].
+
+Always run the tests before submitting pull requests, and ideally run `tox` in order to check that your modifications are compatible with both Python 2 and Python 3, and that they run properly on all supported versions of Django.
+
+Once you've made a pull request take a look at the Travis build status in the GitHub interface and make sure the tests are running as you'd expect.
+
+![Travis status][travis-status]
+
+*Above: Travis build notifications*
+
+## Managing compatibility issues
+
+Sometimes, in order to ensure your code works on various different versions of Django, Python or third party libraries, you'll need to run slightly different code depending on the environment. Any code that branches in this way should be isolated into the `compat.py` module, and should provide a single common interface that the rest of the codebase can use.
# Documentation
The documentation for REST framework is built from the [Markdown][markdown] source files in [the docs directory][docs].
-There are many great markdown editors that make working with the documentation really easy. The [Mou editor for Mac][mou] is one such editor that comes highly recommended.
+There are many great Markdown editors that make working with the documentation really easy. The [Mou editor for Mac][mou] is one such editor that comes highly recommended.
## Building the documentation
-To build the documentation, simply run the `mkdocs.py` script.
+To build the documentation, install MkDocs with `pip install mkdocs` and then run the following command.
- ./mkdocs.py
+ mkdocs build
-This will build the html output into the `html` directory.
+This will build the documentation into the `site` directory.
-You can build the documentation and open a preview in a browser window by using the `-p` flag.
+You can build the documentation and open a preview in a browser window by using the `serve` command.
- ./mkdocs.py -p
+ mkdocs serve
## Language style
@@ -76,8 +151,7 @@ Documentation should be in American English. The tone of the documentation is v
Some other tips:
* Keep paragraphs reasonably short.
-* Use double spacing after the end of sentences.
-* Don't use the abbreviations such as 'e.g..' but instead use long form, such as 'For example'.
+* Don't use abbreviations such as 'e.g.' but instead use the long form, such as 'For example'.
## Markdown style
@@ -88,8 +162,8 @@ There are a couple of conventions you should follow when working on the document
Headers should use the hash style. For example:
### Some important topic
-
-The underline style should not be used. **Don't do this:**
+
+The underline style should not be used. **Don't do this:**
Some important topic
====================
@@ -99,9 +173,9 @@ The underline style should not be used. **Don't do this:**
Links should always use the reference style, with the referenced hyperlinks kept at the end of the document.
Here is a link to [some other thing][other-thing].
-
+
More text...
-
+
[other-thing]: http://example.com/other/thing
This style helps keep the documentation source consistent and readable.
@@ -110,33 +184,28 @@ If you are hyperlinking to another REST framework document, you should use a rel
[authentication]: ../api-guide/authentication.md
-Linking in this style means you'll be able to click the hyperlink in your markdown editor to open the referenced document. When the documentation is built, these links will be converted into regular links to HTML pages.
+Linking in this style means you'll be able to click the hyperlink in your Markdown editor to open the referenced document. When the documentation is built, these links will be converted into regular links to HTML pages.
##### 3. Notes
If you want to draw attention to a note or warning, use a pair of enclosing lines, like so:
---
-
- **Note:** Make sure you do this thing.
-
- ---
-# Third party packages
+ **Note:** A useful documentation note.
-* Django reusable app
-
-# Core committers
+ ---
-* Still use pull reqs
-* Credits
[cite]: http://www.w3.org/People/Berners-Lee/FAQ.html
-[github]: https://github.com/tomchristie/django-rest-framework
-[django-packages]: https://www.djangopackages.com/grids/g/api/
+[code-of-conduct]: https://www.djangoproject.com/conduct/
[google-group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework
[so-filter]: http://stackexchange.com/filters/66475/rest-framework
[issues]: https://github.com/tomchristie/django-rest-framework/issues?state=open
+[pep-8]: http://www.python.org/dev/peps/pep-0008/
+[travis-status]: ../img/travis-status.png
+[pull-requests]: https://help.github.com/articles/using-pull-requests
+[tox]: http://tox.readthedocs.org/en/latest/
[markdown]: http://daringfireball.net/projects/markdown/basics
[docs]: https://github.com/tomchristie/django-rest-framework/tree/master/docs
[mou]: http://mouapp.com/
diff --git a/docs/topics/credits.md b/docs/topics/credits.md
deleted file mode 100644
index 95cac717..00000000
--- a/docs/topics/credits.md
+++ /dev/null
@@ -1,340 +0,0 @@
-# Credits
-
-The following people have helped make REST framework great.
-
-* Tom Christie - [tomchristie]
-* Marko Tibold - [markotibold]
-* Paul Miller - [paulmillr]
-* Sébastien Piquemal - [sebpiq]
-* Carmen Wick - [cwick]
-* Alex Ehlke - [aehlke]
-* Alen Mujezinovic - [flashingpumpkin]
-* Carles Barrobés - [txels]
-* Michael Fötsch - [mfoetsch]
-* David Larlet - [david]
-* Andrew Straw - [astraw]
-* Zeth - [zeth]
-* Fernando Zunino - [fzunino]
-* Jens Alm - [ulmus]
-* Craig Blaszczyk - [jakul]
-* Garcia Solero - [garciasolero]
-* Tom Drummond - [devioustree]
-* Danilo Bargen - [dbrgn]
-* Andrew McCloud - [amccloud]
-* Thomas Steinacher - [thomasst]
-* Meurig Freeman - [meurig]
-* Anthony Nemitz - [anemitz]
-* Ewoud Kohl van Wijngaarden - [ekohl]
-* Michael Ding - [yandy]
-* Mjumbe Poe - [mjumbewu]
-* Natim - [natim]
-* Sebastian Żurek - [sebzur]
-* Benoit C - [dzen]
-* Chris Pickett - [bunchesofdonald]
-* Ben Timby - [btimby]
-* Michele Lazzeri - [michelelazzeri-nextage]
-* Camille Harang - [mammique]
-* Paul Oswald - [poswald]
-* Sean C. Farley - [scfarley]
-* Daniel Izquierdo - [izquierdo]
-* Can Yavuz - [tschan]
-* Shawn Lewis - [shawnlewis]
-* Alec Perkins - [alecperkins]
-* Michael Barrett - [phobologic]
-* Mathieu Dhondt - [laundromat]
-* Johan Charpentier - [cyberj]
-* Jamie Matthews - [j4mie]
-* Mattbo - [mattbo]
-* Max Hurl - [maximilianhurl]
-* Tomi Pajunen - [eofs]
-* Rob Dobson - [rdobson]
-* Daniel Vaca Araujo - [diviei]
-* Madis Väin - [madisvain]
-* Stephan Groß - [minddust]
-* Pavel Savchenko - [asfaltboy]
-* Otto Yiu - [ottoyiu]
-* Jacob Magnusson - [jmagnusson]
-* Osiloke Harold Emoekpere - [osiloke]
-* Michael Shepanski - [mjs7231]
-* Toni Michel - [tonimichel]
-* Ben Konrath - [benkonrath]
-* Marc Aymerich - [glic3rinu]
-* Ludwig Kraatz - [ludwigkraatz]
-* Rob Romano - [robromano]
-* Eugene Mechanism - [mechanism]
-* Jonas Liljestrand - [jonlil]
-* Justin Davis - [irrelative]
-* Dustin Bachrach - [dbachrach]
-* Mark Shirley - [maspwr]
-* Olivier Aubert - [oaubert]
-* Yuri Prezument - [yprez]
-* Fabian Buechler - [fabianbuechler]
-* Mark Hughes - [mhsparks]
-* Michael van de Waeter - [mvdwaeter]
-* Reinout van Rees - [reinout]
-* Michael Richards - [justanotherbody]
-* Ben Roberts - [roberts81]
-* Venkata Subramanian Mahalingam - [annacoder]
-* George Kappel - [gkappel]
-* Colin Murtaugh - [cmurtaugh]
-* Simon Pantzare - [pilt]
-* Szymon Teżewski - [sunscrapers]
-* Joel Marcotte - [joual]
-* Trey Hunner - [treyhunner]
-* Roman Akinfold - [akinfold]
-* Toran Billups - [toranb]
-* Sébastien Béal - [sebastibe]
-* Andrew Hankinson - [ahankinson]
-* Juan Riaza - [juanriaza]
-* Michael Mior - [michaelmior]
-* Marc Tamlyn - [mjtamlyn]
-* Richard Wackerbarth - [wackerbarth]
-* Johannes Spielmann - [shezi]
-* James Cleveland - [radiosilence]
-* Steve Gregory - [steve-gregory]
-* Federico Capoano - [nemesisdesign]
-* Bruno Renié - [brutasse]
-* Kevin Stone - [kevinastone]
-* Guglielmo Celata - [guglielmo]
-* Mike Tums - [mktums]
-* Michael Elovskikh - [wronglink]
-* Michał Jaworski - [swistakm]
-* Andrea de Marco - [z4r]
-* Fernando Rocha - [fernandogrd]
-* Xavier Ordoquy - [xordoquy]
-* Adam Wentz - [floppya]
-* Andreas Pelme - [pelme]
-* Ryan Detzel - [ryanrdetzel]
-* Omer Katz - [thedrow]
-* Wiliam Souza - [waa]
-* Jonas Braun - [iekadou]
-* Ian Dash - [bitmonkey]
-* Bouke Haarsma - [bouke]
-* Pierre Dulac - [dulaccc]
-* Dave Kuhn - [kuhnza]
-* Sitong Peng - [stoneg]
-* Victor Shih - [vshih]
-* Atle Frenvik Sveen - [atlefren]
-* J Paul Reed - [preed]
-* Matt Majewski - [forgingdestiny]
-* Jerome Chen - [chenjyw]
-* Andrew Hughes - [eyepulp]
-* Daniel Hepper - [dhepper]
-* Hamish Campbell - [hamishcampbell]
-* Marlon Bailey - [avinash240]
-* James Summerfield - [jsummerfield]
-* Andy Freeland - [rouge8]
-* Craig de Stigter - [craigds]
-* Pablo Recio - [pyriku]
-* Brian Zambrano - [brianz]
-* Òscar Vilaplana - [grimborg]
-* Ryan Kaskel - [ryankask]
-* Andy McKay - [andymckay]
-* Matteo Suppo - [matteosuppo]
-* Karol Majta - [lolek09]
-* David Jones - [commonorgarden]
-* Andrew Tarzwell - [atarzwell]
-* Michal Dvořák - [mikee2185]
-* Markus Törnqvist - [mjtorn]
-* Pascal Borreli - [pborreli]
-* Alex Burgel - [aburgel]
-* David Medina - [copitux]
-* Areski Belaid - [areski]
-* Ethan Freman - [mindlace]
-* David Sanders - [davesque]
-* Philip Douglas - [freakydug]
-* Igor Kalat - [trwired]
-* Rudolf Olah - [omouse]
-* Gertjan Oude Lohuis - [gertjanol]
-* Matthias Jacob - [cyroxx]
-* Pavel Zinovkin - [pzinovkin]
-* Will Kahn-Greene - [willkg]
-* Kevin Brown - [kevin-brown]
-* Rodrigo Martell - [coderigo]
-
-Many thanks to everyone who's contributed to the project.
-
-## Additional thanks
-
-The documentation is built with [Bootstrap] and [Markdown].
-
-Project hosting is with [GitHub].
-
-Continuous integration testing is managed with [Travis CI][travis-ci].
-
-The [live sandbox][sandbox] is hosted on [Heroku].
-
-Various inspiration taken from the [Rails], [Piston], [Tastypie], [Dagny] and [django-viewsets] projects.
-
-Development of REST framework 2.0 was sponsored by [DabApps].
-
-## Contact
-
-For usage questions please see the [REST framework discussion group][group].
-
-You can also contact [@_tomchristie][twitter] directly on twitter.
-
-[twitter]: http://twitter.com/_tomchristie
-[bootstrap]: http://twitter.github.com/bootstrap/
-[markdown]: http://daringfireball.net/projects/markdown/
-[github]: https://github.com/tomchristie/django-rest-framework
-[travis-ci]: https://secure.travis-ci.org/tomchristie/django-rest-framework
-[rails]: http://rubyonrails.org/
-[piston]: https://bitbucket.org/jespern/django-piston
-[tastypie]: https://github.com/toastdriven/django-tastypie
-[dagny]: https://github.com/zacharyvoase/dagny
-[django-viewsets]: https://github.com/BertrandBordage/django-viewsets
-[dabapps]: http://lab.dabapps.com
-[sandbox]: http://restframework.herokuapp.com/
-[heroku]: http://www.heroku.com/
-[group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework
-
-[tomchristie]: https://github.com/tomchristie
-[markotibold]: https://github.com/markotibold
-[paulmillr]: https://github.com/paulmillr
-[sebpiq]: https://github.com/sebpiq
-[cwick]: https://github.com/cwick
-[aehlke]: https://github.com/aehlke
-[flashingpumpkin]: https://github.com/flashingpumpkin
-[txels]: https://github.com/txels
-[mfoetsch]: https://github.com/mfoetsch
-[david]: https://github.com/david
-[astraw]: https://github.com/astraw
-[zeth]: https://github.com/zeth
-[fzunino]: https://github.com/fzunino
-[ulmus]: https://github.com/ulmus
-[jakul]: https://github.com/jakul
-[garciasolero]: https://github.com/garciasolero
-[devioustree]: https://github.com/devioustree
-[dbrgn]: https://github.com/dbrgn
-[amccloud]: https://github.com/amccloud
-[thomasst]: https://github.com/thomasst
-[meurig]: https://github.com/meurig
-[anemitz]: https://github.com/anemitz
-[ekohl]: https://github.com/ekohl
-[yandy]: https://github.com/yandy
-[mjumbewu]: https://github.com/mjumbewu
-[natim]: https://github.com/natim
-[sebzur]: https://github.com/sebzur
-[dzen]: https://github.com/dzen
-[bunchesofdonald]: https://github.com/bunchesofdonald
-[btimby]: https://github.com/btimby
-[michelelazzeri-nextage]: https://github.com/michelelazzeri-nextage
-[mammique]: https://github.com/mammique
-[poswald]: https://github.com/poswald
-[scfarley]: https://github.com/scfarley
-[izquierdo]: https://github.com/izquierdo
-[tschan]: https://github.com/tschan
-[shawnlewis]: https://github.com/shawnlewis
-[alecperkins]: https://github.com/alecperkins
-[phobologic]: https://github.com/phobologic
-[laundromat]: https://github.com/laundromat
-[cyberj]: https://github.com/cyberj
-[j4mie]: https://github.com/j4mie
-[mattbo]: https://github.com/mattbo
-[maximilianhurl]: https://github.com/maximilianhurl
-[eofs]: https://github.com/eofs
-[rdobson]: https://github.com/rdobson
-[diviei]: https://github.com/diviei
-[madisvain]: https://github.com/madisvain
-[minddust]: https://github.com/minddust
-[asfaltboy]: https://github.com/asfaltboy
-[ottoyiu]: https://github.com/OttoYiu
-[jmagnusson]: https://github.com/jmagnusson
-[osiloke]: https://github.com/osiloke
-[mjs7231]: https://github.com/mjs7231
-[tonimichel]: https://github.com/tonimichel
-[benkonrath]: https://github.com/benkonrath
-[glic3rinu]: https://github.com/glic3rinu
-[ludwigkraatz]: https://github.com/ludwigkraatz
-[robromano]: https://github.com/robromano
-[mechanism]: https://github.com/mechanism
-[jonlil]: https://github.com/jonlil
-[irrelative]: https://github.com/irrelative
-[dbachrach]: https://github.com/dbachrach
-[maspwr]: https://github.com/maspwr
-[oaubert]: https://github.com/oaubert
-[yprez]: https://github.com/yprez
-[fabianbuechler]: https://github.com/fabianbuechler
-[mhsparks]: https://github.com/mhsparks
-[mvdwaeter]: https://github.com/mvdwaeter
-[reinout]: https://github.com/reinout
-[justanotherbody]: https://github.com/justanotherbody
-[roberts81]: https://github.com/roberts81
-[annacoder]: https://github.com/annacoder
-[gkappel]: https://github.com/gkappel
-[cmurtaugh]: https://github.com/cmurtaugh
-[pilt]: https://github.com/pilt
-[sunscrapers]: https://github.com/sunscrapers
-[joual]: https://github.com/joual
-[treyhunner]: https://github.com/treyhunner
-[akinfold]: https://github.com/akinfold
-[toranb]: https://github.com/toranb
-[sebastibe]: https://github.com/sebastibe
-[ahankinson]: https://github.com/ahankinson
-[juanriaza]: https://github.com/juanriaza
-[michaelmior]: https://github.com/michaelmior
-[mjtamlyn]: https://github.com/mjtamlyn
-[wackerbarth]: https://github.com/wackerbarth
-[shezi]: https://github.com/shezi
-[radiosilence]: https://github.com/radiosilence
-[steve-gregory]: https://github.com/steve-gregory
-[nemesisdesign]: https://github.com/nemesisdesign
-[brutasse]: https://github.com/brutasse
-[kevinastone]: https://github.com/kevinastone
-[guglielmo]: https://github.com/guglielmo
-[mktums]: https://github.com/mktums
-[wronglink]: https://github.com/wronglink
-[swistakm]: https://github.com/swistakm
-[z4r]: https://github.com/z4r
-[fernandogrd]: https://github.com/fernandogrd
-[xordoquy]: https://github.com/xordoquy
-[floppya]: https://github.com/floppya
-[pelme]: https://github.com/pelme
-[ryanrdetzel]: https://github.com/ryanrdetzel
-[thedrow]: https://github.com/thedrow
-[waa]: https://github.com/wiliamsouza
-[iekadou]: https://github.com/iekadou
-[bitmonkey]: https://github.com/bitmonkey
-[bouke]: https://github.com/bouke
-[dulaccc]: https://github.com/dulaccc
-[kuhnza]: https://github.com/kuhnza
-[stoneg]: https://github.com/stoneg
-[vshih]: https://github.com/vshih
-[atlefren]: https://github.com/atlefren
-[preed]: https://github.com/preed
-[forgingdestiny]: https://github.com/forgingdestiny
-[chenjyw]: https://github.com/chenjyw
-[eyepulp]: https://github.com/eyepulp
-[dhepper]: https://github.com/dhepper
-[hamishcampbell]: https://github.com/hamishcampbell
-[avinash240]: https://github.com/avinash240
-[jsummerfield]: https://github.com/jsummerfield
-[rouge8]: https://github.com/rouge8
-[craigds]: https://github.com/craigds
-[pyriku]: https://github.com/pyriku
-[brianz]: https://github.com/brianz
-[grimborg]: https://github.com/grimborg
-[ryankask]: https://github.com/ryankask
-[andymckay]: https://github.com/andymckay
-[matteosuppo]: https://github.com/matteosuppo
-[lolek09]: https://github.com/lolek09
-[commonorgarden]: https://github.com/commonorgarden
-[atarzwell]: https://github.com/atarzwell
-[mikee2185]: https://github.com/mikee2185
-[mjtorn]: https://github.com/mjtorn
-[pborreli]: https://github.com/pborreli
-[aburgel]: https://github.com/aburgel
-[copitux]: https://github.com/copitux
-[areski]: https://github.com/areski
-[mindlace]: https://github.com/mindlace
-[davesque]: https://github.com/davesque
-[freakydug]: https://github.com/freakydug
-[trwired]: https://github.com/trwired
-[omouse]: https://github.com/omouse
-[gertjanol]: https://github.com/gertjanol
-[cyroxx]: https://github.com/cyroxx
-[pzinovkin]: https://github.com/pzinovkin
-[coderigo]: https://github.com/coderigo
-[willkg]: https://github.com/willkg
-[kevin-brown]: https://github.com/kevin-brown
diff --git a/docs/topics/documenting-your-api.md b/docs/topics/documenting-your-api.md
index 7ee538f5..d65e251f 100644
--- a/docs/topics/documenting-your-api.md
+++ b/docs/topics/documenting-your-api.md
@@ -16,7 +16,7 @@ The most common way to document Web APIs today is to produce documentation that
Marc Gibbons' [Django REST Swagger][django-rest-swagger] integrates REST framework with the [Swagger][swagger] API documentation tool. The package produces well presented API documentation, and includes interactive tools for testing API endpoints.
-The pacakge is fully documented, well supported, and comes highly recommended.
+The package is fully documented, well supported, and comes highly recommended.
Django REST Swagger supports REST framework versions 2.3 and above.
@@ -42,7 +42,7 @@ There are various other online tools and services for providing API documentatio
## Self describing APIs
-The browsable API that REST framwork provides makes it possible for your API to be entirely self describing. The documentation for each API endpoint can be provided simply by visiting the URL in your browser.
+The browsable API that REST framework provides makes it possible for your API to be entirely self describing. The documentation for each API endpoint can be provided simply by visiting the URL in your browser.
![Screenshot - Self describing API][image-self-describing-api]
@@ -54,7 +54,7 @@ The title that is used in the browsable API is generated from the view class nam
For example, the view `UserListView`, will be named `User List` when presented in the browsable API.
-When working with viewsets, an appropriate suffix is appended to each generated view. For example, the view set `UserViewSet` will generate views named `User List` and `User Instance`.
+When working with viewsets, an appropriate suffix is appended to each generated view. For example, the view set `UserViewSet` will generate views named `User List` and `User Instance`.
#### Setting the description
@@ -65,9 +65,9 @@ If the python `markdown` library is installed, then [markdown syntax][markdown]
class AccountListView(views.APIView):
"""
Returns a list of all **active** accounts in the system.
-
+
For more details on how accounts are activated please [see here][ref].
-
+
[ref]: http://example.com/activating-accounts
"""
@@ -84,7 +84,7 @@ You can modify the response behavior to `OPTIONS` requests by overriding the `me
def metadata(self, request):
"""
Don't include the view description in OPTIONS responses.
- """
+ """
data = super(ExampleView, self).metadata(request)
data.pop('description')
return data
@@ -93,11 +93,11 @@ You can modify the response behavior to `OPTIONS` requests by overriding the `me
## The hypermedia approach
-To be fully RESTful an API should present it's available actions as hypermedia controls in the responses that it sends.
+To be fully RESTful an API should present its available actions as hypermedia controls in the responses that it sends.
-In this approach, rather than documenting the available API endpoints up front, the description instead concentrates on the *media types* that are used. The available actions take may be taken on any given URL are not strictly fixed, but are instead made available by the presence of link and form controls in the returned document.
+In this approach, rather than documenting the available API endpoints up front, the description instead concentrates on the *media types* that are used. The available actions that may be taken on any given URL are not strictly fixed, but are instead made available by the presence of link and form controls in the returned document.
-To implement a hypermedia API you'll need to decide on an appropriate media type for the API, and implement a custom renderer and parser for that media type. The [REST, Hypermedia & HATEOAS][hypermedia-docs] section of the documention includes pointers to background reading, as well as links to various hypermedia formats.
+To implement a hypermedia API you'll need to decide on an appropriate media type for the API, and implement a custom renderer and parser for that media type. The [REST, Hypermedia & HATEOAS][hypermedia-docs] section of the documentation includes pointers to background reading, as well as links to various hypermedia formats.
[cite]: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
[django-rest-swagger]: https://github.com/marcgibbons/django-rest-swagger
diff --git a/docs/topics/internationalization.md b/docs/topics/internationalization.md
new file mode 100644
index 00000000..3968e23d
--- /dev/null
+++ b/docs/topics/internationalization.md
@@ -0,0 +1,113 @@
+# Internationalization
+
+> Supporting internationalization is not optional. It must be a core feature.
+>
+> &mdash; [Jannis Leidel, speaking at Django Under the Hood, 2015][cite].
+
+REST framework ships with translatable error messages. You can make these appear in your language enabling [Django's standard translation mechanisms][django-translation].
+
+Doing so will allow you to:
+
+* Select a language other than English as the default, using the standard `LANGUAGE_CODE` Django setting.
+* Allow clients to choose a language themselves, using the `LocaleMiddleware` included with Django. A typical usage for API clients would be to include an `Accept-Language` request header.
+
+## Enabling internationalized APIs
+
+You can change the default language by using the standard Django `LANGUAGE_CODE` setting:
+
+ LANGUAGE_CODE = "es-es"
+
+You can turn on per-request language requests by adding `LocalMiddleware` to your `MIDDLEWARE_CLASSES` setting:
+
+ MIDDLEWARE_CLASSES = [
+ ...
+ 'django.middleware.locale.LocaleMiddleware'
+ ]
+
+When per-request internationalization is enabled, client requests will respect the `Accept-Language` header where possible. For example, let's make a request for an unsupported media type:
+
+**Request**
+
+ GET /api/users HTTP/1.1
+ Accept: application/xml
+ Accept-Language: es-es
+ Host: example.org
+
+**Response**
+
+ HTTP/1.0 406 NOT ACCEPTABLE
+
+ {"detail": "No se ha podido satisfacer la solicitud de cabecera de Accept."}
+
+REST framework includes these built-in translations both for standard exception cases, and for serializer validation errors.
+
+Note that the translations only apply to the error strings themselves. The format of error messages, and the keys of field names will remain the same. An example `400 Bad Request` response body might look like this:
+
+ {"detail": {"username": ["Esse campo deve ser unico."]}}
+
+If you want to use different string for parts of the response such as `detail` and `non_field_errors` then you can modify this behavior by using a [custom exception handler][custom-exception-handler].
+
+#### Specifying the set of supported languages.
+
+By default all available languages will be supported.
+
+If you only wish to support a subset of the available languages, use Django's standard `LANGUAGES` setting:
+
+ LANGUAGES = [
+ ('de', _('German')),
+ ('en', _('English')),
+ ]
+
+## Adding new translations
+
+REST framework translations are managed online using [Transifex][transifex-project]. You can use the Transifex service to add new translation languages. The maintenance team will then ensure that these translation strings are included in the REST framework package.
+
+Sometimes you may need to add translation strings to your project locally. You may need to do this if:
+
+* You want to use REST Framework in a language which has not been translated yet on Transifex.
+* Your project includes custom error messages, which are not part of REST framework's default translation strings.
+
+#### Translating a new language locally
+
+This guide assumes you are already familiar with how to translate a Django app. If you're not, start by reading [Django's translation docs][django-translation].
+
+If you're translating a new language you'll need to translate the existing REST framework error messages:
+
+1. Make a new folder where you want to store the internationalization resources. Add this path to your [`LOCALE_PATHS`][django-locale-paths] setting.
+
+2. Now create a subfolder for the language you want to translate. The folder should be named using [locale name][django-locale-name] notation. For example: `de`, `pt_BR`, `es_AR`.
+
+3. Now copy the [base translations file][django-po-source] from the REST framework source code into your translations folder.
+
+4. Edit the `django.po` file you've just copied, translating all the error messages.
+
+5. Run `manage.py compilemessages -l pt_BR` to make the translations
+available for Django to use. You should see a message like `processing file django.po in <...>/locale/pt_BR/LC_MESSAGES`.
+
+6. Restart your development server to see the changes take effect.
+
+If you're only translating custom error messages that exist inside your project codebase you don't need to copy the REST framework source `django.po` file into a `LOCALE_PATHS` folder, and can instead simply run Django's standard `makemessages` process.
+
+## How the language is determined
+
+If you want to allow per-request language preferences you'll need to include `django.middleware.locale.LocaleMiddleware` in your `MIDDLEWARE_CLASSES` setting.
+
+You can find more information on how the language preference is determined in the [Django documentation][django-language-preference]. For reference, the method is:
+
+1. First, it looks for the language prefix in the requested URL.
+2. Failing that, it looks for the `LANGUAGE_SESSION_KEY` key in the current user’s session.
+3. Failing that, it looks for a cookie.
+4. Failing that, it looks at the `Accept-Language` HTTP header.
+5. Failing that, it uses the global `LANGUAGE_CODE` setting.
+
+For API clients the most appropriate of these will typically be to use the `Accept-Language` header; Sessions and cookies will not be available unless using session authentication, and generally better practice to prefer an `Accept-Language` header for API clients rather than using language URL prefixes.
+
+[cite]: http://youtu.be/Wa0VfS2q94Y
+[django-translation]: https://docs.djangoproject.com/en/1.7/topics/i18n/translation
+[custom-exception-handler]: ../api-guide/exceptions.md#custom-exception-handling
+[transifex-project]: https://www.transifex.com/projects/p/django-rest-framework/
+[django-po-source]: https://raw.githubusercontent.com/tomchristie/django-rest-framework/master/rest_framework/locale/en_US/LC_MESSAGES/django.po
+[django-language-preference]: https://docs.djangoproject.com/en/1.7/topics/i18n/translation/#how-django-discovers-language-preference
+[django-locale-paths]: https://docs.djangoproject.com/en/1.7/ref/settings/#std:setting-LOCALE_PATHS
+[django-locale-name]: https://docs.djangoproject.com/en/1.7/topics/i18n/#term-locale-name
+[contributing]: ../../CONTRIBUTING.md
diff --git a/docs/topics/kickstarter-announcement.md b/docs/topics/kickstarter-announcement.md
new file mode 100644
index 00000000..78c5cce6
--- /dev/null
+++ b/docs/topics/kickstarter-announcement.md
@@ -0,0 +1,163 @@
+# Kickstarting Django REST framework 3
+
+---
+
+<iframe style="display: block; margin: 0 auto 0 auto" width="480" height="360" src="https://www.kickstarter.com/projects/tomchristie/django-rest-framework-3/widget/video.html" frameborder="0" scrolling="no"> </iframe>
+
+---
+
+In order to continue to drive the project forward, I'm launching a Kickstarter campaign to help fund the development of a major new release - Django REST framework 3.
+
+## Project details
+
+This new release will allow us to comprehensively address some of the shortcomings of the framework, and will aim to include the following:
+
+* Faster, simpler and easier-to-use serializers.
+* An alternative admin-style interface for the browsable API.
+* Search and filtering controls made accessible in the browsable API.
+* Alternative API pagination styles.
+* Documentation around API versioning.
+* Triage of outstanding tickets.
+* Improving the ongoing quality and maintainability of the project.
+
+Full details are available now on the [project page](https://www.kickstarter.com/projects/tomchristie/django-rest-framework-3).
+
+If you're interested in helping make sustainable open source development a reality please [visit the Kickstarter page](https://www.kickstarter.com/projects/tomchristie/django-rest-framework-3) and consider funding the project.
+
+I can't wait to see where this takes us!
+
+Many thanks to everyone for your support so far,
+
+ Tom Christie :)
+
+---
+
+## Sponsors
+
+We've now blazed way past all our goals, with a staggering £30,000 (~$50,000), meaning I'll be in a position to work on the project significantly beyond what we'd originally planned for. I owe a huge debt of gratitude to all the wonderful companies and individuals who have been backing the project so generously, and making this possible.
+
+---
+
+### Platinum sponsors
+
+Our platinum sponsors have each made a hugely substantial contribution to the future development of Django REST framework, and I simply can't thank them enough.
+
+<ul class="sponsor diamond">
+<li><a href="https://www.eventbrite.com/" rel="nofollow" style="background-image:url(../../img/sponsors/0-eventbrite.png);">Eventbrite</a></li>
+</ul>
+
+<ul class="sponsor platinum">
+<li><a href="https://www.divio.ch/" rel="nofollow" style="background-image:url(../../img/sponsors/1-divio.png);">Divio</a></li>
+<li><a href="http://company.onlulu.com/en/" rel="nofollow" style="background-image:url(../../img/sponsors/1-lulu.png);">Lulu</a></li>
+<li><a href="https://p.ota.to/" rel="nofollow" style="background-image:url(../../img/sponsors/1-potato.png);">Potato</a></li>
+<li><a href="http://www.wiredrive.com/" rel="nofollow" style="background-image:url(../../img/sponsors/1-wiredrive.png);">Wiredrive</a></li>
+<li><a href="http://www.cyaninc.com/" rel="nofollow" style="background-image:url(../../img/sponsors/1-cyan.png);">Cyan</a></li>
+<li><a href="https://www.runscope.com/" rel="nofollow" style="background-image:url(../../img/sponsors/1-runscope.png);">Runscope</a></li>
+<li><a href="http://simpleenergy.com/" rel="nofollow" style="background-image:url(../../img/sponsors/1-simple-energy.png);">Simple Energy</a></li>
+<li><a href="http://vokalinteractive.com/" rel="nofollow" style="background-image:url(../../img/sponsors/1-vokal_interactive.png);">VOKAL Interactive</a></li>
+<li><a href="http://www.purplebit.com/" rel="nofollow" style="background-image:url(../../img/sponsors/1-purplebit.png);">Purple Bit</a></li>
+<li><a href="http://www.kuwaitnet.net/" rel="nofollow" style="background-image:url(../../img/sponsors/1-kuwaitnet.png);">KuwaitNET</a></li>
+</ul>
+
+<div style="clear: both"></div>
+
+---
+
+### Gold sponsors
+
+Our gold sponsors include companies large and small. Many thanks for their significant funding of the project and their commitment to sustainable open-source development.
+
+<ul class="sponsor gold">
+<li><a href="https://laterpay.net/" rel="nofollow" style="background-image:url(../../img/sponsors/2-laterpay.png);">LaterPay</a></li>
+<li><a href="https://www.schubergphilis.com/" rel="nofollow" style="background-image:url(../../img/sponsors/2-schuberg_philis.png);">Schuberg Philis</a></li>
+<li><a href="http://prorenata.se/" rel="nofollow" style="background-image:url(../../img/sponsors/2-prorenata.png);">ProReNata AB</a></li>
+<li><a href="https://www.sgawebsites.com/" rel="nofollow" style="background-image:url(../../img/sponsors/2-sga.png);">SGA Websites</a></li>
+<li><a href="http://www.sirono.com/" rel="nofollow" style="background-image:url(../../img/sponsors/2-sirono.png);">Sirono</a></li>
+<li><a href="http://www.vinta.com.br/" rel="nofollow" style="background-image:url(../../img/sponsors/2-vinta.png);">Vinta Software Studio</a></li>
+<li><a href="http://www.rapasso.nl/index.php/en" rel="nofollow" style="background-image:url(../../img/sponsors/2-rapasso.png);">Rapasso</a></li>
+<li><a href="https://mirusresearch.com/" rel="nofollow" style="background-image:url(../../img/sponsors/2-mirus_research.png);">Mirus Research</a></li>
+<li><a href="http://hipolabs.com" rel="nofollow" style="background-image:url(../../img/sponsors/2-hipo.png);">Hipo</a></li>
+<li><a href="http://www.byte.nl" rel="nofollow" style="background-image:url(../../img/sponsors/2-byte.png);">Byte</a></li>
+<li><a href="http://lightningkite.com/" rel="nofollow" style="background-image:url(../../img/sponsors/2-lightning_kite.png);">Lightning Kite</a></li>
+<li><a href="https://opbeat.com/" rel="nofollow" style="background-image:url(../../img/sponsors/2-opbeat.png);">Opbeat</a></li>
+<li><a href="https://koordinates.com" rel="nofollow" style="background-image:url(../../img/sponsors/2-koordinates.png);">Koordinates</a></li>
+<li><a href="http://pulsecode.ca" rel="nofollow" style="background-image:url(../../img/sponsors/2-pulsecode.png);">Pulsecode Inc.</a></li>
+<li><a href="http://singinghorsestudio.com" rel="nofollow" style="background-image:url(../../img/sponsors/2-singing-horse.png);">Singing Horse Studio Ltd.</a></li>
+<li><a href="https://www.heroku.com/" rel="nofollow" style="background-image:url(../../img/sponsors/2-heroku.png);">Heroku</a></li>
+<li><a href="https://www.rheinwerk-verlag.de/" rel="nofollow" style="background-image:url(../../img/sponsors/2-rheinwerk_verlag.png);">Rheinwerk Verlag</a></li>
+<li><a href="http://www.securitycompass.com/" rel="nofollow" style="background-image:url(../../img/sponsors/2-security_compass.png);">Security Compass</a></li>
+<li><a href="https://www.djangoproject.com/foundation/" rel="nofollow" style="background-image:url(../../img/sponsors/2-django.png);">Django Software Foundation</a></li>
+<li><a href="http://www.hipflaskapp.com" rel="nofollow" style="background-image:url(../../img/sponsors/2-hipflask.png);">Hipflask</a></li>
+<li><a href="http://www.crate.io/" rel="nofollow" style="background-image:url(../../img/sponsors/2-crate.png);">Crate</a></li>
+<li><a href="http://crypticocorp.com/" rel="nofollow" style="background-image:url(../../img/sponsors/2-cryptico.png);">Cryptico Corp</a></li>
+<li><a href="http://www.nexthub.com/" rel="nofollow" style="background-image:url(../../img/sponsors/2-nexthub.png);">NextHub</a></li>
+<li><a href="https://www.compile.com/" rel="nofollow" style="background-image:url(../../img/sponsors/2-compile.png);">Compile</a></li>
+<li><a href="http://wusawork.org" rel="nofollow" style="background-image:url(../../img/sponsors/2-wusawork.png);">WusaWork</a></li>
+<li><a href="http://envisionlinux.org/blog" rel="nofollow">Envision Linux</a></li>
+</ul>
+
+<div style="clear: both; padding-bottom: 40px;"></div>
+
+---
+
+### Silver sponsors
+
+The serious financial contribution that our silver sponsors have made is very much appreciated. I'd like to say a particular thank&nbsp;you to individuals who have choosen to privately support the project at this level.
+
+<ul class="sponsor silver">
+<li><a href="http://www.imtapps.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-imt_computer_services.png);">IMT Computer Services</a></li>
+<li><a href="http://wildfish.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-wildfish.png);">Wildfish</a></li>
+<li><a href="http://www.thermondo.de/" rel="nofollow" style="background-image:url(../../img/sponsors/3-thermondo-gmbh.png);">Thermondo GmbH</a></li>
+<li><a href="http://providenz.fr/" rel="nofollow" style="background-image:url(../../img/sponsors/3-providenz.png);">Providenz</a></li>
+<li><a href="https://www.alwaysdata.com" rel="nofollow" style="background-image:url(../../img/sponsors/3-alwaysdata.png);">alwaysdata.com</a></li>
+<li><a href="http://www.triggeredmessaging.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-triggered_messaging.png);">Triggered Messaging</a></li>
+<li><a href="https://www.ipushpull.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-ipushpull.png);">PushPull Technology Ltd</a></li>
+<li><a href="http://www.transcode.de/" rel="nofollow" style="background-image:url(../../img/sponsors/3-transcode.png);">Transcode</a></li>
+<li><a href="https://garfo.io/" rel="nofollow" style="background-image:url(../../img/sponsors/3-garfo.png);">Garfo</a></li>
+<li><a href="https://goshippo.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-shippo.png);">Shippo</a></li>
+<li><a href="http://www.gizmag.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-gizmag.png);">Gizmag</a></li>
+<li><a href="http://www.tivix.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-tivix.png);">Tivix</a></li>
+<li><a href="http://www.safaribooksonline.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-safari.png);">Safari</a></li>
+<li><a href="http://brightloop.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-brightloop.png);">Bright Loop</a></li>
+<li><a href="http://www.aba-systems.com.au/" rel="nofollow" style="background-image:url(../../img/sponsors/3-aba.png);">ABA Systems</a></li>
+<li><a href="http://beefarm.ru/" rel="nofollow" style="background-image:url(../../img/sponsors/3-beefarm.png);">beefarm.ru</a></li>
+<li><a href="http://www.vzzual.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-vzzual.png);">Vzzual.com</a></li>
+<li><a href="http://infinite-code.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-infinite_code.png);">Infinite Code</a></li>
+<li><a href="http://crosswordtracker.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-crosswordtracker.png);">Crossword Tracker</a></li>
+<li><a href="https://www.pkgfarm.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-pkgfarm.png);">PkgFarm</a></li>
+<li><a href="http://life.tl/" rel="nofollow" style="background-image:url(../../img/sponsors/3-life_the_game.png);">Life. The Game.</a></li>
+<li><a href="http://blimp.io/" rel="nofollow" style="background-image:url(../../img/sponsors/3-blimp.png);">Blimp</a></li>
+<li><a href="http://pathwright.com" rel="nofollow" style="background-image:url(../../img/sponsors/3-pathwright.png);">Pathwright</a></li>
+<li><a href="http://fluxility.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-fluxility.png);">Fluxility</a></li>
+<li><a href="http://teonite.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-teonite.png);">Teonite</a></li>
+<li><a href="http://trackmaven.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-trackmaven.png);">TrackMaven</a></li>
+<li><a href="http://www.phurba.net/" rel="nofollow" style="background-image:url(../../img/sponsors/3-phurba.png);">Phurba</a></li>
+<li><a href="http://www.nephila.co.uk/" rel="nofollow" style="background-image:url(../../img/sponsors/3-nephila.png);">Nephila</a></li>
+<li><a href="http://www.aditium.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-aditium.png);">Aditium</a></li>
+<li><a href="http://www.eyesopen.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-openeye.png);">OpenEye Scientific Software</a></li>
+<li><a href="https://holvi.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-holvi.png);">Holvi</a></li>
+<li><a href="http://cantemo.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-cantemo.gif);">Cantemo</a></li>
+<li><a href="https://www.makespace.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-makespace.png);">MakeSpace</a></li>
+<li><a href="https://www.ax-semantics.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-ax_semantics.png);">AX Semantics</a></li>
+<li><a href="http://istrategylabs.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-isl.png);">ISL</a></li>
+</ul>
+
+<div style="clear: both; padding-bottom: 40px;"></div>
+
+**Individual backers**: Paul Hallett, <a href="http://www.paulwhippconsulting.com/">Paul Whipp</a>, Dylan Roy, Jannis Leidel, <a href="https://linovia.com/en/">Xavier Ordoquy</a>, <a href="http://spielmannsolutions.com/">Johannes Spielmann</a>, <a href="http://brooklynhacker.com/">Rob Spectre</a>, <a href="http://chrisheisel.com/">Chris Heisel</a>, Marwan Alsabbagh, Haris Ali, Tuomas Toivonen.
+
+---
+
+### Advocates
+
+The following individuals made a significant financial contribution to the development of Django REST framework 3, for which I can only offer a huge, warm and sincere thank you!
+
+**Individual backers**: Jure Cuhalev, Kevin Brolly, Ferenc Szalai, Dougal Matthews, Stefan Foulis, Carlos Hernando, Alen Mujezinovic, Ross Crawford-d'Heureuse, George Kappel, Alasdair Nicol, John Carr, Steve Winton, Trey, Manuel Miranda, David Horn, Vince Mi, Daniel Sears, Jamie Matthews, Ryan Currah, Marty Kemka, Scott Nixon, Moshin Elahi, Kevin Campbell, Jose Antonio Leiva Izquierdo, Kevin Stone, Andrew Godwin, Tijs Teulings, Roger Boardman, Xavier Antoviaque, Darian Moody, Lujeni, Jon Dugan, Wiley Kestner, Daniel C. Silverstein, Daniel Hahler, Subodh Nijsure, Philipp Weidenhiller, Yusuke Muraoka, Danny Roa, Reto Aebersold, Kyle Getrost, Décébal Hormuz, James Dacosta, Matt Long, Mauro Rocco, Tyrel Souza, Ryan Campbell, Ville Jyrkkä, Charalampos Papaloizou, Nikolai Røed Kristiansen, Antoni Aloy López, Celia Oakley, Michał Krawczak, Ivan VenOsdel, Tim Watts, Martin Warne, Nicola Jordan, Ryan Kaskel.
+
+**Corporate backers**: Savannah Informatics, Prism Skylabs, Musical Operating Devices.
+
+---
+
+### Supporters
+
+There were also almost 300 further individuals choosing to help fund the project at other levels or choosing to give anonymously. Again, thank you, thank you, thank you!
diff --git a/docs/topics/project-management.md b/docs/topics/project-management.md
new file mode 100644
index 00000000..4926f355
--- /dev/null
+++ b/docs/topics/project-management.md
@@ -0,0 +1,200 @@
+# Project management
+
+> "No one can whistle a symphony; it takes a whole orchestra to play it"
+>
+> &mdash; Halford E. Luccock
+
+This document outlines our project management processes for REST framework.
+
+The aim is to ensure that the project has a high
+["bus factor"][bus-factor], and can continue to remain well supported for the foreseeable future. Suggestions for improvements to our process are welcome.
+
+---
+
+## Maintenance team
+
+We have a quarterly maintenance cycle where new members may join the maintenance team. We currently cap the size of the team at 5 members, and may encourage folks to step out of the team for a cycle to allow new members to participate.
+
+#### Current team
+
+The [maintenance team for Q1 2015](https://github.com/tomchristie/django-rest-framework/issues/2190):
+
+* [@tomchristie](https://github.com/tomchristie/)
+* [@xordoquy](https://github.com/xordoquy/) (Release manager.)
+* [@carltongibson](https://github.com/carltongibson/)
+* [@kevin-brown](https://github.com/kevin-brown/)
+* [@jpadilla](https://github.com/jpadilla/)
+
+#### Maintenance cycles
+
+Each maintenance cycle is initiated by an issue being opened with the `Process` label.
+
+* To be considered for a maintainer role simply comment against the issue.
+* Existing members must explicitly opt-in to the next cycle by check-marking their name.
+* The final decision on the incoming team will be made by `@tomchristie`.
+
+Members of the maintenance team will be added as collaborators to the repository.
+
+The following template should be used for the description of the issue, and serves as the formal process for selecting the team.
+
+ This issue is for determining the maintenance team for the *** period.
+
+ Please see the [Project management](http://www.django-rest-framework.org/topics/project-management/) section of our documentation for more details.
+
+ ---
+
+ #### Renewing existing members.
+
+ The following people are the current maintenance team. Please checkmark your name if you wish to continue to have write permission on the repository for the *** period.
+
+ - [ ] @***
+ - [ ] @***
+ - [ ] @***
+ - [ ] @***
+ - [ ] @***
+
+ ---
+
+ #### New members.
+
+ If you wish to be considered for this or a future date, please comment against this or subsequent issues.
+
+ To modify this process for future maintenance cycles make a pull request to the [project management](http://www.django-rest-framework.org/topics/project-management/) documentation.
+
+#### Responsibilities of team members
+
+Team members have the following responsibilities.
+
+* Close invalid or resolved tickets.
+* Add triage labels and milestones to tickets.
+* Merge finalized pull requests.
+* Build and deploy the documentation, using `mkdocs gh-deploy`.
+* Build and update the included translation packs.
+
+Further notes for maintainers:
+
+* Code changes should come in the form of a pull request - do not push directly to master.
+* Maintainers should typically not merge their own pull requests.
+* Each issue/pull request should have exactly one label once triaged.
+* Search for un-triaged issues with [is:open no:label][un-triaged].
+
+It should be noted that participating actively in the REST framework project clearly **does not require being part of the maintenance team**. Almost every import part of issue triage and project improvement can be actively worked on regardless of your collaborator status on the repository.
+
+---
+
+## Release process
+
+The release manager is selected on every quarterly maintenance cycle.
+
+* The manager should be selected by `@tomchristie`.
+* The manager will then have the maintainer role added to PyPI package.
+* The previous manager will then have the maintainer role removed from the PyPI package.
+
+Our PyPI releases will be handled by either the current release manager, or by `@tomchristie`. Every release should have an open issue tagged with the `Release` label and marked against the appropriate milestone.
+
+The following template should be used for the description of the issue, and serves as a release checklist.
+
+ Release manager is @***.
+ Pull request is #***.
+
+ Checklist:
+
+ - [ ] Create pull request for [release notes](https://github.com/tomchristie/django-rest-framework/blob/master/docs/topics/release-notes.md) based on the [*.*.* milestone](https://github.com/tomchristie/django-rest-framework/milestones/***).
+ - [ ] Ensure the pull request increments the version to `*.*.*` in [`restframework/__init__.py`](https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/__init__.py).
+ - [ ] Confirm with @tomchristie that release is finalized and ready to go.
+ - [ ] Ensure that release date is included in pull request.
+ - [ ] Merge the release pull request.
+ - [ ] Push the package to PyPI with `./setup.py publish`.
+ - [ ] Tag the release, with `git tag -a *.*.* -m 'version *.*.*'; git push --tags`.
+ - [ ] Deploy the documentation with `mkdocs gh-deploy`.
+ - [ ] Make a release announcement on the [discussion group](https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework).
+ - [ ] Make a release announcement on twitter.
+ - [ ] Close the milestone on GitHub.
+
+ To modify this process for future releases make a pull request to the [project management](http://www.django-rest-framework.org/topics/project-management/) documentation.
+
+When pushing the release to PyPI ensure that your environment has been installed from our development `requirement.txt`, so that documentation and PyPI installs are consistently being built against a pinned set of packages.
+
+---
+
+## Translations
+
+The maintenance team are responsible for managing the translation packs include in REST framework. Translating the source strings into multiple languages is managed through the [transifex service][transifex-project].
+
+### Managing Transifex
+
+The [official Transifex client][transifex-client] is used to upload and download translations to Transifex. The client is installed using pip:
+
+ pip install transifex-client
+
+To use it you'll need a login to Transifex which has a password, and you'll need to have administrative access to the Transifex project. You'll need to create a `~/.transifexrc` file which contains your credentials.
+
+ [https://www.transifex.com]
+ username = ***
+ token = ***
+ password = ***
+ hostname = https://www.transifex.com
+
+### Upload new source files
+
+When any user visible strings are changed, they should be uploaded to Transifex so that the translators can start to translate them. To do this, just run:
+
+ # 1. Update the source django.po file, which is the US English version.
+ cd rest_framework
+ django-admin.py makemessages -l en_US
+ # 2. Push the source django.po file to Transifex.
+ cd ..
+ tx push -s
+
+When pushing source files, Transifex will update the source strings of a resource to match those from the new source file.
+
+Here's how differences between the old and new source files will be handled:
+
+* New strings will be added.
+* Modified strings will be added as well.
+* Strings which do not exist in the new source file will be removed from the database, along with their translations. If that source strings gets re-added later then [Transifex Translation Memory][translation-memory] will automatically include the translation string.
+
+### Download translations
+
+When a translator has finished translating their work needs to be downloaded from Transifex into the REST framework repository. To do this, run:
+
+ # 3. Pull the translated django.po files from Transifex.
+ tx pull -a
+ cd rest_framework
+ # 4. Compile the binary .mo files for all supported languages.
+ django-admin.py compilemessages
+
+---
+
+## Project requirements
+
+All our test requirements are pinned to exact versions, in order to ensure that our test runs are reproducible. We maintain the requirements in the `requirements` directory. The requirements files are referenced from the `tox.ini` configuration file, ensuring we have a single source of truth for package versions used in testing.
+
+Package upgrades should generally be treated as isolated pull requests. You can check if there are any packages available at a newer version, by using the `pip list --outdated`.
+
+---
+
+## Project ownership
+
+The PyPI package is owned by `@tomchristie`. As a backup `@j4mie` also has ownership of the package.
+
+If `@tomchristie` ceases to participate in the project then `@j4mie` has responsibility for handing over ownership duties.
+
+#### Outstanding management & ownership issues
+
+The following issues still need to be addressed:
+
+* [Consider moving the repo into a proper GitHub organization][github-org].
+* Ensure `@jamie` has back-up access to the `django-rest-framework.org` domain setup and admin.
+* Document ownership of the [live example][sandbox] API.
+* Document ownership of the [mailing list][mailing-list] and IRC channel.
+* Document ownership and management of the security mailing list.
+
+[bus-factor]: http://en.wikipedia.org/wiki/Bus_factor
+[un-triaged]: https://github.com/tomchristie/django-rest-framework/issues?q=is%3Aopen+no%3Alabel
+[transifex-project]: https://www.transifex.com/projects/p/django-rest-framework/
+[transifex-client]: https://pypi.python.org/pypi/transifex-client
+[translation-memory]: http://docs.transifex.com/guides/tm#let-tm-automatically-populate-translations
+[github-org]: https://github.com/tomchristie/django-rest-framework/issues/2162
+[sandbox]: http://restframework.herokuapp.com/
+[mailing-list]: https://groups.google.com/forum/#!forum/django-rest-framework
diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md
index d379ab74..35592feb 100644
--- a/docs/topics/release-notes.md
+++ b/docs/topics/release-notes.md
@@ -10,7 +10,7 @@ Minor version numbers (0.0.x) are used for changes that are API compatible. You
Medium version numbers (0.x.0) may include API changes, in line with the [deprecation policy][deprecation-policy]. You should read the release notes carefully before upgrading between medium point releases.
-Major version numbers (x.0.0) are reserved for substantial project milestones. No major point releases are currently planned.
+Major version numbers (x.0.0) are reserved for substantial project milestones.
## Deprecation policy
@@ -38,516 +38,224 @@ You can determine your currently installed version using `pip freeze`:
---
-## 2.3.x series
-
-### 2.3.6
-
-**Date**: 27th June 2013
-
-* Added `trailing_slash` option to routers.
-* Include support for `HttpStreamingResponse`.
-* Support wider range of default serializer validation when used with custom model fields.
-* UTF-8 Support for browsable API descriptions.
-* OAuth2 provider uses timezone aware datetimes when supported.
-* Bugfix: Return error correctly when OAuth non-existent consumer occurs.
-* Bugfix: Allow `FileUploadParser` to correctly filename if provided as URL kwarg.
-* Bugfix: Fix `ScopedRateThrottle`.
-
-### 2.3.5
-
-**Date**: 3rd June 2013
-
-* Added `get_url` hook to `HyperlinkedIdentityField`.
-* Serializer field `default` argument may be a callable.
-* `@action` decorator now accepts a `methods` argument.
-* Bugfix: `request.user` should be still be accessible in renderer context if authentication fails.
-* Bugfix: The `lookup_field` option on `HyperlinkedIdentityField` should apply by default to the url field on the serializer.
-* Bugfix: `HyperlinkedIdentityField` should continue to support `pk_url_kwarg`, `slug_url_kwarg`, `slug_field`, in a pending deprecation state.
-* Bugfix: Ensure we always return 404 instead of 500 if a lookup field cannot be converted to the correct lookup type. (Eg non-numeric `AutoInteger` pk lookup)
-
-### 2.3.4
-
-**Date**: 24th May 2013
-
-* Serializer fields now support `label` and `help_text`.
-* Added `UnicodeJSONRenderer`.
-* `OPTIONS` requests now return metadata about fields for `POST` and `PUT` requests.
-* Bugfix: `charset` now properly included in `Content-Type` of responses.
-* Bugfix: Blank choice now added in browsable API on nullable relationships.
-* Bugfix: Many to many relationships with `through` tables are now read-only.
-* Bugfix: Serializer fields now respect model field args such as `max_length`.
-* Bugfix: SlugField now performs slug validation.
-* Bugfix: Lazy-translatable strings now properly serialized.
-* Bugfix: Browsable API now supports bootswatch styles properly.
-* Bugfix: HyperlinkedIdentityField now uses `lookup_field` kwarg.
-
-**Note**: Responses now correctly include an appropriate charset on the `Content-Type` header. For example: `application/json; charset=utf-8`. If you have tests that check the content type of responses, you may need to update these accordingly.
-
-### 2.3.3
-
-**Date**: 16th May 2013
-
-* Added SearchFilter
-* Added OrderingFilter
-* Added GenericViewSet
-* Bugfix: Multiple `@action` and `@link` methods now allowed on viewsets.
-* Bugfix: Fix API Root view issue with DjangoModelPermissions
-
-### 2.3.2
-
-**Date**: 8th May 2013
-
-* Bugfix: Fix `TIME_FORMAT`, `DATETIME_FORMAT` and `DATE_FORMAT` settings.
-* Bugfix: Fix `DjangoFilterBackend` issue, failing when used on view with queryset attribute.
-
-### 2.3.1
-
-**Date**: 7th May 2013
-
-* Bugfix: Fix breadcrumb rendering issue.
-
-### 2.3.0
-
-**Date**: 7th May 2013
-
-* ViewSets and Routers.
-* ModelSerializers support reverse relations in 'fields' option.
-* HyperLinkedModelSerializers support 'id' field in 'fields' option.
-* Cleaner generic views.
-* Support for multiple filter classes.
-* FileUploadParser support for raw file uploads.
-* DecimalField support.
-* Made Login template easier to restyle.
-* Bugfix: Fix issue with depth>1 on ModelSerializer.
-
-**Note**: See the [2.3 announcement][2.3-announcement] for full details.
-
----
-
-## 2.2.x series
-
-### 2.2.7
-
-**Date**: 17th April 2013
-
-* Loud failure when view does not return a `Response` or `HttpResponse`.
-* Bugfix: Fix for Django 1.3 compatibility.
-* Bugfix: Allow overridden `get_object()` to work correctly.
-
-### 2.2.6
-
-**Date**: 4th April 2013
-
-* OAuth2 authentication no longer requires unnecessary URL parameters in addition to the token.
-* URL hyperlinking in browsable API now handles more cases correctly.
-* Long HTTP headers in browsable API are broken in multiple lines when possible.
-* Bugfix: Fix regression with DjangoFilterBackend not worthing correctly with single object views.
-* Bugfix: OAuth should fail hard when invalid token used.
-* Bugfix: Fix serializer potentially returning `None` object for models that define `__bool__` or `__len__`.
-
-### 2.2.5
-
-**Date**: 26th March 2013
-
-* Serializer support for bulk create and bulk update operations.
-* Regression fix: Date and time fields return date/time objects by default. Fixes regressions caused by 2.2.2. See [#743][743] for more details.
-* Bugfix: Fix 500 error is OAuth not attempted with OAuthAuthentication class installed.
-* `Serializer.save()` now supports arbitrary keyword args which are passed through to the object `.save()` method. Mixins use `force_insert` and `force_update` where appropriate, resulting in one less database query.
-
-### 2.2.4
-
-**Date**: 13th March 2013
-
-* OAuth 2 support.
-* OAuth 1.0a support.
-* Support X-HTTP-Method-Override header.
-* Filtering backends are now applied to the querysets for object lookups as well as lists. (Eg you can use a filtering backend to control which objects should 404)
-* Deal with error data nicely when deserializing lists of objects.
-* Extra override hook to configure `DjangoModelPermissions` for unauthenticated users.
-* Bugfix: Fix regression which caused extra database query on paginated list views.
-* Bugfix: Fix pk relationship bug for some types of 1-to-1 relations.
-* Bugfix: Workaround for Django bug causing case where `Authtoken` could be registered for cascade delete from `User` even if not installed.
-
-### 2.2.3
-
-**Date**: 7th March 2013
-
-* Bugfix: Fix None values for for `DateField`, `DateTimeField` and `TimeField`.
-
-### 2.2.2
-
-**Date**: 6th March 2013
-
-* Support for custom input and output formats for `DateField`, `DateTimeField` and `TimeField`.
-* Cleanup: Request authentication is no longer lazily evaluated, instead authentication is always run, which results in more consistent, obvious behavior. Eg. Supplying bad auth credentials will now always return an error response, even if no permissions are set on the view.
-* Bugfix for serializer data being uncacheable with pickle protocol 0.
-* Bugfixes for model field validation edge-cases.
-* Bugfix for authtoken migration while using a custom user model and south.
-
-### 2.2.1
-
-**Date**: 22nd Feb 2013
-
-* Security fix: Use `defusedxml` package to address XML parsing vulnerabilities.
-* Raw data tab added to browsable API. (Eg. Allow for JSON input.)
-* Added TimeField.
-* Serializer fields can be mapped to any method that takes no args, or only takes kwargs which have defaults.
-* Unicode support for view names/descriptions in browsable API.
-* Bugfix: request.DATA should return an empty `QueryDict` with no data, not `None`.
-* Bugfix: Remove unneeded field validation, which caused extra queries.
-
-**Security note**: Following the [disclosure of security vulnerabilities][defusedxml-announce] in Python's XML parsing libraries, use of the `XMLParser` class now requires the `defusedxml` package to be installed.
-
-The security vulnerabilities only affect APIs which use the `XMLParser` class, by enabling it in any views, or by having it set in the `DEFAULT_PARSER_CLASSES` setting. Note that the `XMLParser` class is not enabled by default, so this change should affect a minority of users.
-
-### 2.2.0
-
-**Date**: 13th Feb 2013
-
-* Python 3 support.
-* Added a `post_save()` hook to the generic views.
-* Allow serializers to handle dicts as well as objects.
-* Deprecate `ManyRelatedField()` syntax in favor of `RelatedField(many=True)`
-* Deprecate `null=True` on relations in favor of `required=False`.
-* Deprecate `blank=True` on CharFields, just use `required=False`.
-* Deprecate optional `obj` argument in permissions checks in favor of `has_object_permission`.
-* Deprecate implicit hyperlinked relations behavior.
-* Bugfix: Fix broken DjangoModelPermissions.
-* Bugfix: Allow serializer output to be cached.
-* Bugfix: Fix styling on browsable API login.
-* Bugfix: Fix issue with deserializing empty to-many relations.
-* Bugfix: Ensure model field validation is still applied for ModelSerializer subclasses with an custom `.restore_object()` method.
-
-**Note**: See the [2.2 announcement][2.2-announcement] for full details.
-
----
-
-## 2.1.x series
-
-### 2.1.17
-
-**Date**: 26th Jan 2013
-
-* Support proper 401 Unauthorized responses where appropriate, instead of always using 403 Forbidden.
-* Support json encoding of timedelta objects.
-* `format_suffix_patterns()` now supports `include` style URL patterns.
-* Bugfix: Fix issues with custom pagination serializers.
-* Bugfix: Nested serializers now accept `source='*'` argument.
-* Bugfix: Return proper validation errors when incorrect types supplied for relational fields.
-* Bugfix: Support nullable FKs with `SlugRelatedField`.
-* Bugfix: Don't call custom validation methods if the field has an error.
-
-**Note**: If the primary authentication class is `TokenAuthentication` or `BasicAuthentication`, a view will now correctly return 401 responses to unauthenticated access, with an appropriate `WWW-Authenticate` header, instead of 403 responses.
-
-### 2.1.16
-
-**Date**: 14th Jan 2013
-
-* Deprecate `django.utils.simplejson` in favor of Python 2.6's built-in json module.
-* Bugfix: `auto_now`, `auto_now_add` and other `editable=False` fields now default to read-only.
-* Bugfix: PK fields now only default to read-only if they are an AutoField or if `editable=False`.
-* Bugfix: Validation errors instead of exceptions when serializers receive incorrect types.
-* Bugfix: Validation errors instead of exceptions when related fields receive incorrect types.
-* Bugfix: Handle ObjectDoesNotExist exception when serializing null reverse one-to-one
-
-**Note**: Prior to 2.1.16, The Decimals would render in JSON using floating point if `simplejson` was installed, but otherwise render using string notation. Now that use of `simplejson` has been deprecated, Decimals will consistently render using string notation. See [#582] for more details.
-
-### 2.1.15
-
-**Date**: 3rd Jan 2013
-
-* Added `PATCH` support.
-* Added `RetrieveUpdateAPIView`.
-* Remove unused internal `save_m2m` flag on `ModelSerializer.save()`.
-* Tweak behavior of hyperlinked fields with an explicit format suffix.
-* Relation changes are now persisted in `.save()` instead of in `.restore_object()`.
-* Bugfix: Fix issue with FileField raising exception instead of validation error when files=None.
-* Bugfix: Partial updates should not set default values if field is not included.
-
-### 2.1.14
-
-**Date**: 31st Dec 2012
-
-* Bugfix: ModelSerializers now include reverse FK fields on creation.
-* Bugfix: Model fields with `blank=True` are now `required=False` by default.
-* Bugfix: Nested serializers now support nullable relationships.
-
-**Note**: From 2.1.14 onwards, relational fields move out of the `fields.py` module and into the new `relations.py` module, in order to separate them from regular data type fields, such as `CharField` and `IntegerField`.
-
-This change will not affect user code, so long as it's following the recommended import style of `from rest_framework import serializers` and referring to fields using the style `serializers.PrimaryKeyRelatedField`.
-
-
-### 2.1.13
-
-**Date**: 28th Dec 2012
-
-* Support configurable `STATICFILES_STORAGE` storage.
-* Bugfix: Related fields now respect the required flag, and may be required=False.
-
-### 2.1.12
-
-**Date**: 21st Dec 2012
-
-* Bugfix: Fix bug that could occur using ChoiceField.
-* Bugfix: Fix exception in browsable API on DELETE.
-* Bugfix: Fix issue where pk was was being set to a string if set by URL kwarg.
-
-### 2.1.11
-
-**Date**: 17th Dec 2012
-
-* Bugfix: Fix issue with M2M fields in browsable API.
-
-### 2.1.10
-
-**Date**: 17th Dec 2012
-
-* Bugfix: Ensure read-only fields don't have model validation applied.
-* Bugfix: Fix hyperlinked fields in paginated results.
-
-### 2.1.9
-
-**Date**: 11th Dec 2012
-
-* Bugfix: Fix broken nested serialization.
-* Bugfix: Fix `Meta.fields` only working as tuple not as list.
-* Bugfix: Edge case if unnecessarily specifying `required=False` on read only field.
-
-### 2.1.8
-
-**Date**: 8th Dec 2012
-
-* Fix for creating nullable Foreign Keys with `''` as well as `None`.
-* Added `null=<bool>` related field option.
-
-### 2.1.7
-
-**Date**: 7th Dec 2012
-
-* Serializers now properly support nullable Foreign Keys.
-* Serializer validation now includes model field validation, such as uniqueness constraints.
-* Support 'true' and 'false' string values for BooleanField.
-* Added pickle support for serialized data.
-* Support `source='dotted.notation'` style for nested serializers.
-* Make `Request.user` settable.
-* Bugfix: Fix `RegexField` to work with `BrowsableAPIRenderer`.
-
-### 2.1.6
-
-**Date**: 23rd Nov 2012
-
-* Bugfix: Unfix DjangoModelPermissions. (I am a doofus.)
-
-### 2.1.5
-
-**Date**: 23rd Nov 2012
-
-* Bugfix: Fix DjangoModelPermissions.
-
-### 2.1.4
-
-**Date**: 22nd Nov 2012
-
-* Support for partial updates with serializers.
-* Added `RegexField`.
-* Added `SerializerMethodField`.
-* Serializer performance improvements.
-* Added `obtain_token_view` to get tokens when using `TokenAuthentication`.
-* Bugfix: Django 1.5 configurable user support for `TokenAuthentication`.
-
-### 2.1.3
-
-**Date**: 16th Nov 2012
-
-* Added `FileField` and `ImageField`. For use with `MultiPartParser`.
-* Added `URLField` and `SlugField`.
-* Support for `read_only_fields` on `ModelSerializer` classes.
-* Support for clients overriding the pagination page sizes. Use the `PAGINATE_BY_PARAM` setting or set the `paginate_by_param` attribute on a generic view.
-* 201 Responses now return a 'Location' header.
-* Bugfix: Serializer fields now respect `max_length`.
-
-### 2.1.2
-
-**Date**: 9th Nov 2012
-
-* **Filtering support.**
-* Bugfix: Support creation of objects with reverse M2M relations.
-
-### 2.1.1
-
-**Date**: 7th Nov 2012
-
-* Support use of HTML exception templates. Eg. `403.html`
-* Hyperlinked fields take optional `slug_field`, `slug_url_kwarg` and `pk_url_kwarg` arguments.
-* Bugfix: Deal with optional trailing slashes properly when generating breadcrumbs.
-* Bugfix: Make textareas same width as other fields in browsable API.
-* Private API change: `.get_serializer` now uses same `instance` and `data` ordering as serializer initialization.
-
-### 2.1.0
-
-**Date**: 5th Nov 2012
-
-* **Serializer `instance` and `data` keyword args have their position swapped.**
-* `queryset` argument is now optional on writable model fields.
-* Hyperlinked related fields optionally take `slug_field` and `slug_url_kwarg` arguments.
-* Support Django's cache framework.
-* Minor field improvements. (Don't stringify dicts, more robust many-pk fields.)
-* Bugfix: Support choice field in Browsable API.
-* Bugfix: Related fields with `read_only=True` do not require a `queryset` argument.
-
-**API-incompatible changes**: Please read [this thread][2.1.0-notes] regarding the `instance` and `data` keyword args before updating to 2.1.0.
-
----
-
-## 2.0.x series
-
-### 2.0.2
-
-**Date**: 2nd Nov 2012
-
-* Fix issues with pk related fields in the browsable API.
-
-### 2.0.1
-
-**Date**: 1st Nov 2012
-
-* Add support for relational fields in the browsable API.
-* Added SlugRelatedField and ManySlugRelatedField.
-* If PUT creates an instance return '201 Created', instead of '200 OK'.
-
-### 2.0.0
-
-**Date**: 30th Oct 2012
-
-* **Fix all of the things.** (Well, almost.)
-* For more information please see the [2.0 announcement][announcement].
-
----
-
-## 0.4.x series
-
-### 0.4.0
-
-* Supports Django 1.5.
-* Fixes issues with 'HEAD' method.
-* Allow views to specify template used by TemplateRenderer
-* More consistent error responses
-* Some serializer fixes
-* Fix internet explorer ajax behavior
-* Minor xml and yaml fixes
-* Improve setup (e.g. use staticfiles, not the defunct ADMIN_MEDIA_PREFIX)
-* Sensible absolute URL generation, not using hacky set_script_prefix
-
----
-
-## 0.3.x series
-
-### 0.3.3
-
-* Added DjangoModelPermissions class to support `django.contrib.auth` style permissions.
-* Use `staticfiles` for css files.
- - Easier to override. Won't conflict with customized admin styles (e.g. grappelli)
-* Templates are now nicely namespaced.
- - Allows easier overriding.
-* Drop implied 'pk' filter if last arg in urlconf is unnamed.
- - Too magical. Explicit is better than implicit.
-* Saner template variable auto-escaping.
-* Tidier setup.py
-* Updated for URLObject 2.0
-* Bugfixes:
- - Bug with PerUserThrottling when user contains unicode chars.
-
-### 0.3.2
-
-* Bugfixes:
- * Fix 403 for POST and PUT from the UI with UserLoggedInAuthentication (#115)
- * serialize_model method in serializer.py may cause wrong value (#73)
- * Fix Error when clicking OPTIONS button (#146)
- * And many other fixes
-* Remove short status codes
- - Zen of Python: "There should be one-- and preferably only one --obvious way to do it."
-* get_name, get_description become methods on the view - makes them overridable.
-* Improved model mixin API - Hooks for build_query, get_instance_data, get_model, get_queryset, get_ordering
-
-### 0.3.1
-
-* [not documented]
-
-### 0.3.0
-
-* JSONP Support
-* Bugfixes, including support for latest markdown release
-
----
-
-## 0.2.x series
-
-### 0.2.4
-
-* Fix broken IsAdminUser permission.
-* OPTIONS support.
-* XMLParser.
-* Drop mentions of Blog, BitBucket.
-
-### 0.2.3
-
-* Fix some throttling bugs.
-* ``X-Throttle`` header on throttling.
-* Support for nesting resources on related models.
-
-### 0.2.2
-
-* Throttling support complete.
-
-### 0.2.1
-
-* Couple of simple bugfixes over 0.2.0
-
-### 0.2.0
-
-* Big refactoring changes since 0.1.0, ask on the discussion group if anything isn't clear.
- The public API has been massively cleaned up. Expect it to be fairly stable from here on in.
-
-* ``Resource`` becomes decoupled into ``View`` and ``Resource``, your views should now inherit from ``View``, not ``Resource``.
-
-* The handler functions on views ``.get() .put() .post()`` etc, no longer have the ``content`` and ``auth`` args.
- Use ``self.CONTENT`` inside a view to access the deserialized, validated content.
- Use ``self.user`` inside a view to access the authenticated user.
-
-* ``allowed_methods`` and ``anon_allowed_methods`` are now defunct. if a method is defined, it's available.
- The ``permissions`` attribute on a ``View`` is now used to provide generic permissions checking.
- Use permission classes such as ``FullAnonAccess``, ``IsAuthenticated`` or ``IsUserOrIsAnonReadOnly`` to set the permissions.
-
-* The ``authenticators`` class becomes ``authentication``. Class names change to ``Authentication``.
-
-* The ``emitters`` class becomes ``renderers``. Class names change to ``Renderers``.
-
-* ``ResponseException`` becomes ``ErrorResponse``.
-
-* The mixin classes have been nicely refactored, the basic mixins are now ``RequestMixin``, ``ResponseMixin``, ``AuthMixin``, and ``ResourceMixin``
- You can reuse these mixin classes individually without using the ``View`` class.
+## 3.0.x series
+
+### 3.1.0
+
+**Date**: [5th March 2015][3.1.0-milestone].
+
+For full details see the [3.1 release announcement](3.1-announcement.md).
+
+### 3.0.5
+
+**Date**: [10th February 2015][3.0.5-milestone].
+
+* Fix a bug where `_closable_objects` breaks pickling. ([#1850][gh1850], [#2492][gh2492])
+* Allow non-standard `User` models with `Throttling`. ([#2524][gh2524])
+* Support custom `User.db_table` in TokenAuthentication migration. ([#2479][gh2479])
+* Fix misleading `AttributeError` tracebacks on `Request` objects. ([#2530][gh2530], [#2108][gh2108])
+* `ManyRelatedField.get_value` clearing field on partial update. ([#2475][gh2475])
+* Removed '.model' shortcut from code. ([#2486][gh2486])
+* Fix `detail_route` and `list_route` mutable argument. ([#2518][gh2518])
+* Prefetching the user object when getting the token in `TokenAuthentication`. ([#2519][gh2519])
+
+### 3.0.4
+
+**Date**: [28th January 2015][3.0.4-milestone].
+
+* Django 1.8a1 support. ([#2425][gh2425], [#2446][gh2446], [#2441][gh2441])
+* Add `DictField` and support Django 1.8 `HStoreField`. ([#2451][gh2451], [#2106][gh2106])
+* Add `UUIDField` and support Django 1.8 `UUIDField`. ([#2448][gh2448], [#2433][gh2433], [#2432][gh2432])
+* `BaseRenderer.render` now raises `NotImplementedError`. ([#2434][gh2434])
+* Fix timedelta JSON serialization on Python 2.6. ([#2430][gh2430])
+* `ResultDict` and `ResultList` now appear as standard dict/list. ([#2421][gh2421])
+* Fix visible `HiddenField` in the HTML form of the web browsable API page. ([#2410][gh2410])
+* Use `OrderedDict` for `RelatedField.choices`. ([#2408][gh2408])
+* Fix ident format when using `HTTP_X_FORWARDED_FOR`. ([#2401][gh2401])
+* Fix invalid key with memcached while using throttling. ([#2400][gh2400])
+* Fix `FileUploadParser` with version 3.x. ([#2399][gh2399])
+* Fix the serializer inheritance. ([#2388][gh2388])
+* Fix caching issues with `ReturnDict`. ([#2360][gh2360])
+
+### 3.0.3
+
+**Date**: [8th January 2015][3.0.3-milestone].
+
+* Fix `MinValueValidator` on `models.DateField`. ([#2369][gh2369])
+* Fix serializer missing context when pagination is used. ([#2355][gh2355])
+* Namespaced router URLs are now supported by the `DefaultRouter`. ([#2351][gh2351])
+* `required=False` allows omission of value for output. ([#2342][gh2342])
+* Use textarea input for `models.TextField`. ([#2340][gh2340])
+* Use custom `ListSerializer` for pagination if required. ([#2331][gh2331], [#2327][gh2327])
+* Better behavior with null and '' for blank HTML fields. ([#2330][gh2330])
+* Ensure fields in `exclude` are model fields. ([#2319][gh2319])
+* Fix `IntegerField` and `max_length` argument incompatibility. ([#2317][gh2317])
+* Fix the YAML encoder for 3.0 serializers. ([#2315][gh2315], [#2283][gh2283])
+* Fix the behavior of empty HTML fields. ([#2311][gh2311], [#1101][gh1101])
+* Fix Metaclass attribute depth ignoring fields attribute. ([#2287][gh2287])
+* Fix `format_suffix_patterns` to work with Django's `i18n_patterns`. ([#2278][gh2278])
+* Ability to customize router URLs for custom actions, using `url_path`. ([#2010][gh2010])
+* Don't install Django REST Framework as egg. ([#2386][gh2386])
+
+### 3.0.2
+
+**Date**: [17th December 2014][3.0.2-milestone].
+
+* Ensure `request.user` is made available to response middleware. ([#2155][gh2155])
+* `Client.logout()` also cancels any existing `force_authenticate`. ([#2218][gh2218], [#2259][gh2259])
+* Extra assertions and better checks to preventing incorrect serializer API use. ([#2228][gh2228], [#2234][gh2234], [#2262][gh2262], [#2263][gh2263], [#2266][gh2266], [#2267][gh2267], [#2289][gh2289], [#2291][gh2291])
+* Fixed `min_length` message for `CharField`. ([#2255][gh2255])
+* Fix `UnicodeDecodeError`, which can occur on serializer `repr`. ([#2270][gh2270], [#2279][gh2279])
+* Fix empty HTML values when a default is provided. ([#2280][gh2280], [#2294][gh2294])
+* Fix `SlugRelatedField` raising `UnicodeEncodeError` when used as a multiple choice input. ([#2290][gh2290])
+
+### 3.0.1
+
+**Date**: [11th December 2014][3.0.1-milestone].
+
+* More helpful error message when the default Serializer `create()` fails. ([#2013][gh2013])
+* Raise error when attempting to save serializer if data is not valid. ([#2098][gh2098])
+* Fix `FileUploadParser` breaks with empty file names and multiple upload handlers. ([#2109][gh2109])
+* Improve `BindingDict` to support standard dict-functions. ([#2135][gh2135], [#2163][gh2163])
+* Add `validate()` to `ListSerializer`. ([#2168][gh2168], [#2225][gh2225], [#2232][gh2232])
+* Fix JSONP renderer failing to escape some characters. ([#2169][gh2169], [#2195][gh2195])
+* Add missing default style for `FileField`. ([#2172][gh2172])
+* Actions are required when calling `ViewSet.as_view()`. ([#2175][gh2175])
+* Add `allow_blank` to `ChoiceField`. ([#2184][gh2184], [#2239][gh2239])
+* Cosmetic fixes in the HTML renderer. ([#2187][gh2187])
+* Raise error if `fields` on serializer is not a list of strings. ([#2193][gh2193], [#2213][gh2213])
+* Improve checks for nested creates and updates. ([#2194][gh2194], [#2196][gh2196])
+* `validated_attrs` argument renamed to `validated_data` in `Serializer` `create()`/`update()`. ([#2197][gh2197])
+* Remove deprecated code to reflect the dropped Django versions. ([#2200][gh2200])
+* Better serializer errors for nested writes. ([#2202][gh2202], [#2215][gh2215])
+* Fix pagination and custom permissions incompatibility. ([#2205][gh2205])
+* Raise error if `fields` on serializer is not a list of strings. ([#2213][gh2213])
+* Add missing translation markers for relational fields. ([#2231][gh2231])
+* Improve field lookup behavior for dicts/mappings. ([#2244][gh2244], [#2243][gh2243])
+* Optimized hyperlinked PK. ([#2242][gh2242])
+
+### 3.0.0
+
+**Date**: 1st December 2014
+
+For full details see the [3.0 release announcement](3.0-announcement.md).
---
-## 0.1.x series
-
-### 0.1.1
-
-* Final build before pulling in all the refactoring changes for 0.2, in case anyone needs to hang on to 0.1.
-
-### 0.1.0
-
-* Initial release.
+For older release notes, [please see the version 2.x documentation](old-release-notes).
[cite]: http://www.catb.org/~esr/writings/cathedral-bazaar/cathedral-bazaar/ar01s04.html
[deprecation-policy]: #deprecation-policy
[django-deprecation-policy]: https://docs.djangoproject.com/en/dev/internals/release-process/#internal-release-deprecation-policy
[defusedxml-announce]: http://blog.python.org/2013/02/announcing-defusedxml-fixes-for-xml.html
-[2.2-announcement]: 2.2-announcement.md
-[2.3-announcement]: 2.3-announcement.md
[743]: https://github.com/tomchristie/django-rest-framework/pull/743
[staticfiles14]: https://docs.djangoproject.com/en/1.4/howto/static-files/#with-a-template-tag
[staticfiles13]: https://docs.djangoproject.com/en/1.3/howto/static-files/#with-a-template-tag
[2.1.0-notes]: https://groups.google.com/d/topic/django-rest-framework/Vv2M0CMY9bg/discussion
-[announcement]: rest-framework-2-announcement.md
-[#582]: https://github.com/tomchristie/django-rest-framework/issues/582
+[ticket-582]: https://github.com/tomchristie/django-rest-framework/issues/582
+[rfc-6266]: http://tools.ietf.org/html/rfc6266#section-4.3
+[old-release-notes]: http://tomchristie.github.io/rest-framework-2-docs/topics/release-notes#24x-series
+
+[3.0.1-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.1+Release%22
+[3.0.2-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.2+Release%22
+[3.0.3-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.3+Release%22
+[3.0.4-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.4+Release%22
+[3.0.5-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.5+Release%22
+
+<!-- 3.0.1 -->
+[gh2013]: https://github.com/tomchristie/django-rest-framework/issues/2013
+[gh2098]: https://github.com/tomchristie/django-rest-framework/issues/2098
+[gh2109]: https://github.com/tomchristie/django-rest-framework/issues/2109
+[gh2135]: https://github.com/tomchristie/django-rest-framework/issues/2135
+[gh2163]: https://github.com/tomchristie/django-rest-framework/issues/2163
+[gh2168]: https://github.com/tomchristie/django-rest-framework/issues/2168
+[gh2169]: https://github.com/tomchristie/django-rest-framework/issues/2169
+[gh2172]: https://github.com/tomchristie/django-rest-framework/issues/2172
+[gh2175]: https://github.com/tomchristie/django-rest-framework/issues/2175
+[gh2184]: https://github.com/tomchristie/django-rest-framework/issues/2184
+[gh2187]: https://github.com/tomchristie/django-rest-framework/issues/2187
+[gh2193]: https://github.com/tomchristie/django-rest-framework/issues/2193
+[gh2194]: https://github.com/tomchristie/django-rest-framework/issues/2194
+[gh2195]: https://github.com/tomchristie/django-rest-framework/issues/2195
+[gh2196]: https://github.com/tomchristie/django-rest-framework/issues/2196
+[gh2197]: https://github.com/tomchristie/django-rest-framework/issues/2197
+[gh2200]: https://github.com/tomchristie/django-rest-framework/issues/2200
+[gh2202]: https://github.com/tomchristie/django-rest-framework/issues/2202
+[gh2205]: https://github.com/tomchristie/django-rest-framework/issues/2205
+[gh2213]: https://github.com/tomchristie/django-rest-framework/issues/2213
+[gh2213]: https://github.com/tomchristie/django-rest-framework/issues/2213
+[gh2215]: https://github.com/tomchristie/django-rest-framework/issues/2215
+[gh2225]: https://github.com/tomchristie/django-rest-framework/issues/2225
+[gh2231]: https://github.com/tomchristie/django-rest-framework/issues/2231
+[gh2232]: https://github.com/tomchristie/django-rest-framework/issues/2232
+[gh2239]: https://github.com/tomchristie/django-rest-framework/issues/2239
+[gh2242]: https://github.com/tomchristie/django-rest-framework/issues/2242
+[gh2243]: https://github.com/tomchristie/django-rest-framework/issues/2243
+[gh2244]: https://github.com/tomchristie/django-rest-framework/issues/2244
+<!-- 3.0.2 -->
+[gh2155]: https://github.com/tomchristie/django-rest-framework/issues/2155
+[gh2218]: https://github.com/tomchristie/django-rest-framework/issues/2218
+[gh2228]: https://github.com/tomchristie/django-rest-framework/issues/2228
+[gh2234]: https://github.com/tomchristie/django-rest-framework/issues/2234
+[gh2255]: https://github.com/tomchristie/django-rest-framework/issues/2255
+[gh2259]: https://github.com/tomchristie/django-rest-framework/issues/2259
+[gh2262]: https://github.com/tomchristie/django-rest-framework/issues/2262
+[gh2263]: https://github.com/tomchristie/django-rest-framework/issues/2263
+[gh2266]: https://github.com/tomchristie/django-rest-framework/issues/2266
+[gh2267]: https://github.com/tomchristie/django-rest-framework/issues/2267
+[gh2270]: https://github.com/tomchristie/django-rest-framework/issues/2270
+[gh2279]: https://github.com/tomchristie/django-rest-framework/issues/2279
+[gh2280]: https://github.com/tomchristie/django-rest-framework/issues/2280
+[gh2289]: https://github.com/tomchristie/django-rest-framework/issues/2289
+[gh2290]: https://github.com/tomchristie/django-rest-framework/issues/2290
+[gh2291]: https://github.com/tomchristie/django-rest-framework/issues/2291
+[gh2294]: https://github.com/tomchristie/django-rest-framework/issues/2294
+<!-- 3.0.3 -->
+[gh1101]: https://github.com/tomchristie/django-rest-framework/issues/1101
+[gh2010]: https://github.com/tomchristie/django-rest-framework/issues/2010
+[gh2278]: https://github.com/tomchristie/django-rest-framework/issues/2278
+[gh2283]: https://github.com/tomchristie/django-rest-framework/issues/2283
+[gh2287]: https://github.com/tomchristie/django-rest-framework/issues/2287
+[gh2311]: https://github.com/tomchristie/django-rest-framework/issues/2311
+[gh2315]: https://github.com/tomchristie/django-rest-framework/issues/2315
+[gh2317]: https://github.com/tomchristie/django-rest-framework/issues/2317
+[gh2319]: https://github.com/tomchristie/django-rest-framework/issues/2319
+[gh2327]: https://github.com/tomchristie/django-rest-framework/issues/2327
+[gh2330]: https://github.com/tomchristie/django-rest-framework/issues/2330
+[gh2331]: https://github.com/tomchristie/django-rest-framework/issues/2331
+[gh2340]: https://github.com/tomchristie/django-rest-framework/issues/2340
+[gh2342]: https://github.com/tomchristie/django-rest-framework/issues/2342
+[gh2351]: https://github.com/tomchristie/django-rest-framework/issues/2351
+[gh2355]: https://github.com/tomchristie/django-rest-framework/issues/2355
+[gh2369]: https://github.com/tomchristie/django-rest-framework/issues/2369
+[gh2386]: https://github.com/tomchristie/django-rest-framework/issues/2386
+<!-- 3.0.4 -->
+[gh2425]: https://github.com/tomchristie/django-rest-framework/issues/2425
+[gh2446]: https://github.com/tomchristie/django-rest-framework/issues/2446
+[gh2441]: https://github.com/tomchristie/django-rest-framework/issues/2441
+[gh2451]: https://github.com/tomchristie/django-rest-framework/issues/2451
+[gh2106]: https://github.com/tomchristie/django-rest-framework/issues/2106
+[gh2448]: https://github.com/tomchristie/django-rest-framework/issues/2448
+[gh2433]: https://github.com/tomchristie/django-rest-framework/issues/2433
+[gh2432]: https://github.com/tomchristie/django-rest-framework/issues/2432
+[gh2434]: https://github.com/tomchristie/django-rest-framework/issues/2434
+[gh2430]: https://github.com/tomchristie/django-rest-framework/issues/2430
+[gh2421]: https://github.com/tomchristie/django-rest-framework/issues/2421
+[gh2410]: https://github.com/tomchristie/django-rest-framework/issues/2410
+[gh2408]: https://github.com/tomchristie/django-rest-framework/issues/2408
+[gh2401]: https://github.com/tomchristie/django-rest-framework/issues/2401
+[gh2400]: https://github.com/tomchristie/django-rest-framework/issues/2400
+[gh2399]: https://github.com/tomchristie/django-rest-framework/issues/2399
+[gh2388]: https://github.com/tomchristie/django-rest-framework/issues/2388
+[gh2360]: https://github.com/tomchristie/django-rest-framework/issues/2360
+<!-- 3.0.5 -->
+[gh1850]: https://github.com/tomchristie/django-rest-framework/issues/1850
+[gh2108]: https://github.com/tomchristie/django-rest-framework/issues/2108
+[gh2475]: https://github.com/tomchristie/django-rest-framework/issues/2475
+[gh2479]: https://github.com/tomchristie/django-rest-framework/issues/2479
+[gh2486]: https://github.com/tomchristie/django-rest-framework/issues/2486
+[gh2492]: https://github.com/tomchristie/django-rest-framework/issues/2492
+[gh2518]: https://github.com/tomchristie/django-rest-framework/issues/2518
+[gh2519]: https://github.com/tomchristie/django-rest-framework/issues/2519
+[gh2524]: https://github.com/tomchristie/django-rest-framework/issues/2524
+[gh2530]: https://github.com/tomchristie/django-rest-framework/issues/2530
diff --git a/docs/topics/rest-framework-2-announcement.md b/docs/topics/rest-framework-2-announcement.md
index f1060d90..ed41bb48 100644
--- a/docs/topics/rest-framework-2-announcement.md
+++ b/docs/topics/rest-framework-2-announcement.md
@@ -1,6 +1,4 @@
-# Django REST framework 2
-
-What it is, and why you should care.
+# Django REST framework 2.0
> Most people just make the mistake that it should be simple to design simple things. In reality, the effort required to design something is inversely proportional to the simplicity of the result.
>
@@ -8,7 +6,7 @@ What it is, and why you should care.
---
-**Announcement:** REST framework 2 released - Tue 30th Oct 2012
+**Announcement:** REST framework 2 released - Tue 30th Oct 2012
---
@@ -37,7 +35,7 @@ REST framework 2 includes a totally re-worked serialization engine, that was ini
* A declarative serialization API, that mirrors Django's `Forms`/`ModelForms` API.
* Structural concerns are decoupled from encoding concerns.
* Able to support rendering and parsing to many formats, including both machine-readable representations and HTML forms.
-* Validation that can be mapped to obvious and comprehensive error responses.
+* Validation that can be mapped to obvious and comprehensive error responses.
* Serializers that support both nested, flat, and partially-nested representations.
* Relationships that can be expressed as primary keys, hyperlinks, slug fields, and other custom representations.
diff --git a/docs/topics/rest-hypermedia-hateoas.md b/docs/topics/rest-hypermedia-hateoas.md
index 3d700488..7e6d2408 100644
--- a/docs/topics/rest-hypermedia-hateoas.md
+++ b/docs/topics/rest-hypermedia-hateoas.md
@@ -1,19 +1,19 @@
# REST, Hypermedia & HATEOAS
-> You keep using that word "REST". I do not think it means what you think it means.
+> You keep using that word "REST". I do not think it means what you think it means.
>
> &mdash; Mike Amundsen, [REST fest 2012 keynote][cite].
-First off, the disclaimer. The name "Django REST framework" was chosen simply to sure the project would be easily found by developers. Throughout the documentation we try to use the more simple and technically correct terminology of "Web APIs".
+First off, the disclaimer. The name "Django REST framework" was decided back in early 2011 and was chosen simply to sure the project would be easily found by developers. Throughout the documentation we try to use the more simple and technically correct terminology of "Web APIs".
-If you are serious about designing a Hypermedia APIs, you should look to resources outside of this documentation to help inform your design choices.
+If you are serious about designing a Hypermedia API, you should look to resources outside of this documentation to help inform your design choices.
The following fall into the "required reading" category.
* Roy Fielding's dissertation - [Architectural Styles and
the Design of Network-based Software Architectures][dissertation].
* Roy Fielding's "[REST APIs must be hypertext-driven][hypertext-driven]" blog post.
-* Leonard Richardson & Sam Ruby's [RESTful Web Services][restful-web-services].
+* Leonard Richardson & Mike Amundsen's [RESTful Web APIs][restful-web-apis].
* Mike Amundsen's [Building Hypermedia APIs with HTML5 and Node][building-hypermedia-apis].
* Steve Klabnik's [Designing Hypermedia APIs][designing-hypermedia-apis].
* The [Richardson Maturity Model][maturitymodel].
@@ -32,12 +32,12 @@ REST framework also includes [serialization] and [parser]/[renderer] components
## What REST framework doesn't provide.
-What REST framework doesn't do is give you is machine readable hypermedia formats such as [HAL][hal], [Collection+JSON][collection], [JSON API][json-api] or HTML [microformats] by default, or the ability to auto-magically create fully HATEOAS style APIs that include hypermedia-based form descriptions and semantically labelled hyperlinks. Doing so would involve making opinionated choices about API design that should really remain outside of the framework's scope.
+What REST framework doesn't do is give you is machine readable hypermedia formats such as [HAL][hal], [Collection+JSON][collection], [JSON API][json-api] or HTML [microformats] by default, or the ability to auto-magically create fully HATEOAS style APIs that include hypermedia-based form descriptions and semantically labelled hyperlinks. Doing so would involve making opinionated choices about API design that should really remain outside of the framework's scope.
[cite]: http://vimeo.com/channels/restfest/page:2
[dissertation]: http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm
[hypertext-driven]: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
-[restful-web-services]: http://www.amazon.com/Restful-Web-Services-Leonard-Richardson/dp/0596529260
+[restful-web-apis]: http://restfulwebapis.org/
[building-hypermedia-apis]: http://www.amazon.com/Building-Hypermedia-APIs-HTML5-Node/dp/1449306578
[designing-hypermedia-apis]: http://designinghypermediaapis.com/
[restisover]: http://blog.steveklabnik.com/posts/2012-02-23-rest-is-over
diff --git a/docs/topics/third-party-resources.md b/docs/topics/third-party-resources.md
new file mode 100644
index 00000000..2f46e1fc
--- /dev/null
+++ b/docs/topics/third-party-resources.md
@@ -0,0 +1,328 @@
+# Third Party Resources
+
+> Software ecosystems […] establish a community that further accelerates the sharing of knowledge, content, issues, expertise and skills.
+>
+> &mdash; [Jan Bosch][cite].
+
+## About Third Party Packages
+
+Third Party Packages allow developers to share code that extends the functionality of Django REST framework, in order to support additional use-cases.
+
+We **support**, **encourage** and **strongly favor** the creation of Third Party Packages to encapsulate new behavior rather than adding additional functionality directly to Django REST Framework.
+
+We aim to make creating third party packages as easy as possible, whilst keeping a **simple** and **well maintained** core API. By promoting third party packages we ensure that the responsibility for a package remains with its author. If a package proves suitably popular it can always be considered for inclusion into the core REST framework.
+
+If you have an idea for a new feature please consider how it may be packaged as a Third Party Package. We're always happy to discuss ideas on the [Mailing List][discussion-group].
+
+## How to create a Third Party Package
+
+### Creating your package
+
+You can use [this cookiecutter template][cookiecutter] for creating reusable Django REST Framework packages quickly. Cookiecutter creates projects from project templates. While optional, this cookiecutter template includes best practices from Django REST framework and other packages, as well as a Travis CI configuration, Tox configuration, and a sane setup.py for easy PyPI registration/distribution.
+
+Note: Let us know if you have an alternate cookiecuter package so we can also link to it.
+
+#### Running the initial cookiecutter command
+
+To run the initial cookiecutter command, you'll first need to install the Python `cookiecutter` package.
+
+ $ pip install cookiecutter
+
+Once `cookiecutter` is installed just run the following to create a new project.
+
+ $ cookiecutter gh:jpadilla/cookiecutter-django-rest-framework
+
+You'll be prompted for some questions, answer them, then it'll create your Python package in the current working directory based on those values.
+
+ full_name (default is "Your full name here")? Johnny Appleseed
+ email (default is "you@example.com")? jappleseed@example.com
+ github_username (default is "yourname")? jappleseed
+ pypi_project_name (default is "dj-package")? djangorestframework-custom-auth
+ repo_name (default is "dj-package")? django-rest-framework-custom-auth
+ app_name (default is "djpackage")? custom_auth
+ project_short_description (default is "Your project description goes here")?
+ year (default is "2014")?
+ version (default is "0.1.0")?
+
+#### Getting it onto GitHub
+
+To put your project up on GitHub, you'll need a repository for it to live in. You can create a new repository [here][new-repo]. If you need help, check out the [Create A Repo][create-a-repo] article on GitHub.
+
+
+#### Adding to Travis CI
+
+We recommend using [Travis CI][travis-ci], a hosted continuous integration service which integrates well with GitHub and is free for public repositories.
+
+To get started with Travis CI, [sign in][travis-ci] with your GitHub account. Once you're signed in, go to your [profile page][travis-profile] and enable the service hook for the repository you want.
+
+If you use the cookiecutter template, your project will already contain a `.travis.yml` file which Travis CI will use to build your project and run tests. By default, builds are triggered everytime you push to your repository or create Pull Request.
+
+#### Uploading to PyPI
+
+Once you've got at least a prototype working and tests running, you should publish it on PyPI to allow others to install it via `pip`.
+
+You must [register][pypi-register] an account before publishing to PyPI.
+
+To register your package on PyPI run the following command.
+
+ $ python setup.py register
+
+If this is the first time publishing to PyPI, you'll be prompted to login.
+
+Note: Before publishing you'll need to make sure you have the latest pip that supports `wheel` as well as install the `wheel` package.
+
+ $ pip install --upgrade pip
+ $ pip install wheel
+
+After this, every time you want to release a new version on PyPI just run the following command.
+
+ $ python setup.py publish
+ You probably want to also tag the version now:
+ git tag -a {0} -m 'version 0.1.0'
+ git push --tags
+
+After releasing a new version to PyPI, it's always a good idea to tag the version and make available as a GitHub Release.
+
+We recommend to follow [Semantic Versioning][semver] for your package's versions.
+
+### Development
+
+#### Version requirements
+
+The cookiecutter template assumes a set of supported versions will be provided for Python and Django. Make sure you correctly update your requirements, docs, `tox.ini`, `.travis.yml`, and `setup.py` to match the set of versions you wish to support.
+
+#### Tests
+
+The cookiecutter template includes a `runtests.py` which uses the `pytest` package as a test runner.
+
+Before running, you'll need to install a couple test requirements.
+
+ $ pip install -r requirements.txt
+
+Once requirements installed, you can run `runtests.py`.
+
+ $ ./runtests.py
+
+Run using a more concise output style.
+
+ $ ./runtests.py -q
+
+Run the tests using a more concise output style, no coverage, no flake8.
+
+ $ ./runtests.py --fast
+
+Don't run the flake8 code linting.
+
+ $ ./runtests.py --nolint
+
+Only run the flake8 code linting, don't run the tests.
+
+ $ ./runtests.py --lintonly
+
+Run the tests for a given test case.
+
+ $ ./runtests.py MyTestCase
+
+Run the tests for a given test method.
+
+ $ ./runtests.py MyTestCase.test_this_method
+
+Shorter form to run the tests for a given test method.
+
+ $ ./runtests.py test_this_method
+
+To run your tests against multiple versions of Python as different versions of requirements such as Django we recommend using `tox`. [Tox][tox-docs] is a generic virtualenv management and test command line tool.
+
+First, install `tox` globally.
+
+ $ pip install tox
+
+To run `tox`, just simply run:
+
+ $ tox
+
+To run a particular `tox` environment:
+
+ $ tox -e envlist
+
+`envlist` is a comma-separated value to that specifies the environments to run tests against. To view a list of all possible test environments, run:
+
+ $ tox -l
+
+#### Version compatibility
+
+Sometimes, in order to ensure your code works on various different versions of Django, Python or third party libraries, you'll need to run slightly different code depending on the environment. Any code that branches in this way should be isolated into a `compat.py` module, and should provide a single common interface that the rest of the codebase can use.
+
+Check out Django REST framework's [compat.py][drf-compat] for an example.
+
+### Once your package is available
+
+Once your package is decently documented and available on PyPI, you might want share it with others that might find it useful.
+
+#### Adding to the Django REST framework grid
+
+We suggest adding your package to the [REST Framework][rest-framework-grid] grid on Django Packages.
+
+#### Adding to the Django REST framework docs
+
+Create a [Pull Request][drf-create-pr] or [Issue][drf-create-issue] on GitHub, and we'll add a link to it from the main REST framework documentation. You can add your package under **Third party packages** of the API Guide section that best applies, like [Authentication][authentication] or [Permissions][permissions]. You can also link your package under the [Third Party Resources][third-party-resources] section.
+
+#### Announce on the discussion group.
+
+You can also let others know about your package through the [discussion group][discussion-group].
+
+## Existing Third Party Packages
+
+Django REST Framework has a growing community of developers, packages, and resources.
+
+Check out a grid detailing all the packages and ecosystem around Django REST Framework at [Django Packages][rest-framework-grid].
+
+To submit new content, [open an issue][drf-create-issue] or [create a pull request][drf-create-pr].
+
+### Authentication
+
+* [djangorestframework-digestauth][djangorestframework-digestauth] - Provides Digest Access Authentication support.
+* [django-oauth-toolkit][django-oauth-toolkit] - Provides OAuth 2.0 support.
+* [doac][doac] - Provides OAuth 2.0 support.
+* [djangorestframework-jwt][djangorestframework-jwt] - Provides JSON Web Token Authentication support.
+* [hawkrest][hawkrest] - Provides Hawk HTTP Authorization.
+* [djangorestframework-httpsignature][djangorestframework-httpsignature] - Provides an easy to use HTTP Signature Authentication mechanism.
+* [djoser][djoser] - Provides a set of views to handle basic actions such as registration, login, logout, password reset and account activation.
+* [django-rest-auth][django-rest-auth] - Provides a set of REST API endpoints for registration, authentication (including social media authentication), password reset, retrieve and update user details, etc.
+
+### Permissions
+
+* [drf-any-permissions][drf-any-permissions] - Provides alternative permission handling.
+* [djangorestframework-composed-permissions][djangorestframework-composed-permissions] - Provides a simple way to define complex permissions.
+* [rest_condition][rest-condition] - Another extension for building complex permissions in a simple and convenient way.
+
+### Serializers
+
+* [django-rest-framework-mongoengine][django-rest-framework-mongoengine] - Serializer class that supports using MongoDB as the storage layer for Django REST framework.
+* [djangorestframework-gis][djangorestframework-gis] - Geographic add-ons
+* [djangorestframework-hstore][djangorestframework-hstore] - Serializer class to support django-hstore DictionaryField model field and its schema-mode feature.
+
+### Serializer fields
+
+* [drf-compound-fields][drf-compound-fields] - Provides "compound" serializer fields, such as lists of simple values.
+* [django-extra-fields][django-extra-fields] - Provides extra serializer fields.
+
+### Views
+
+* [djangorestframework-bulk][djangorestframework-bulk] - Implements generic view mixins as well as some common concrete generic views to allow to apply bulk operations via API requests.
+
+### Routers
+
+* [drf-nested-routers][drf-nested-routers] - Provides routers and relationship fields for working with nested resources.
+* [wq.db.rest][wq.db.rest] - Provides an admin-style model registration API with reasonable default URLs and viewsets.
+
+### Parsers
+
+* [djangorestframework-msgpack][djangorestframework-msgpack] - Provides MessagePack renderer and parser support.
+* [djangorestframework-camel-case][djangorestframework-camel-case] - Provides camel case JSON renderers and parsers.
+
+### Renderers
+
+* [djangorestframework-csv][djangorestframework-csv] - Provides CSV renderer support.
+* [drf_ujson][drf_ujson] - Implements JSON rendering using the UJSON package.
+* [rest-pandas][rest-pandas] - Pandas DataFrame-powered renderers including Excel, CSV, and SVG formats.
+
+### Filtering
+
+* [djangorestframework-chain][djangorestframework-chain] - Allows arbitrary chaining of both relations and lookup filters.
+
+### Misc
+
+* [djangorestrelationalhyperlink][djangorestrelationalhyperlink] - A hyperlinked serialiser that can can be used to alter relationships via hyperlinks, but otherwise like a hyperlink model serializer.
+* [django-rest-swagger][django-rest-swagger] - An API documentation generator for Swagger UI.
+* [django-rest-framework-proxy][django-rest-framework-proxy] - Proxy to redirect incoming request to another API server.
+* [gaiarestframework][gaiarestframework] - Utils for django-rest-framewok
+* [drf-extensions][drf-extensions] - A collection of custom extensions
+* [ember-django-adapter][ember-django-adapter] - An adapter for working with Ember.js
+
+## Other Resources
+
+### Tutorials
+
+* [Beginner's Guide to the Django Rest Framework][beginners-guide-to-the-django-rest-framework]
+* [Getting Started with Django Rest Framework and AngularJS][getting-started-with-django-rest-framework-and-angularjs]
+* [End to end web app with Django-Rest-Framework & AngularJS][end-to-end-web-app-with-django-rest-framework-angularjs]
+* [Start Your API - django-rest-framework part 1][start-your-api-django-rest-framework-part-1]
+* [Permissions & Authentication - django-rest-framework part 2][permissions-authentication-django-rest-framework-part-2]
+* [ViewSets and Routers - django-rest-framework part 3][viewsets-and-routers-django-rest-framework-part-3]
+* [Django Rest Framework User Endpoint][django-rest-framework-user-endpoint]
+* [Check credentials using Django Rest Framework][check-credentials-using-django-rest-framework]
+
+### Videos
+
+* [Ember and Django Part 1 (Video)][ember-and-django-part 1-video]
+* [Django Rest Framework Part 1 (Video)][django-rest-framework-part-1-video]
+* [Pyowa July 2013 - Django Rest Framework (Video)][pyowa-july-2013-django-rest-framework-video]
+* [django-rest-framework and angularjs (Video)][django-rest-framework-and-angularjs-video]
+
+### Articles
+
+* [Web API performance: profiling Django REST framework][web-api-performance-profiling-django-rest-framework]
+* [API Development with Django and Django REST Framework][api-development-with-django-and-django-rest-framework]
+
+[cite]: http://www.software-ecosystems.com/Software_Ecosystems/Ecosystems.html
+[cookiecutter]: https://github.com/jpadilla/cookiecutter-django-rest-framework
+[new-repo]: https://github.com/new
+[create-a-repo]: https://help.github.com/articles/create-a-repo/
+[travis-ci]: https://travis-ci.org
+[travis-profile]: https://travis-ci.org/profile
+[pypi-register]: https://pypi.python.org/pypi?%3Aaction=register_form
+[semver]: http://semver.org/
+[tox-docs]: https://tox.readthedocs.org/en/latest/
+[drf-compat]: https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/compat.py
+[rest-framework-grid]: https://www.djangopackages.com/grids/g/django-rest-framework/
+[drf-create-pr]: https://github.com/tomchristie/django-rest-framework/compare
+[drf-create-issue]: https://github.com/tomchristie/django-rest-framework/issues/new
+[authentication]: ../api-guide/authentication.md
+[permissions]: ../api-guide/permissions.md
+[discussion-group]: https://groups.google.com/forum/#!forum/django-rest-framework
+[djangorestframework-digestauth]: https://github.com/juanriaza/django-rest-framework-digestauth
+[django-oauth-toolkit]: https://github.com/evonove/django-oauth-toolkit
+[doac]: https://github.com/Rediker-Software/doac
+[djangorestframework-jwt]: https://github.com/GetBlimp/django-rest-framework-jwt
+[hawkrest]: https://github.com/kumar303/hawkrest
+[djangorestframework-httpsignature]: https://github.com/etoccalino/django-rest-framework-httpsignature
+[djoser]: https://github.com/sunscrapers/djoser
+[drf-any-permissions]: https://github.com/kevin-brown/drf-any-permissions
+[djangorestframework-composed-permissions]: https://github.com/niwibe/djangorestframework-composed-permissions
+[rest-condition]: https://github.com/caxap/rest_condition
+[django-rest-framework-mongoengine]: https://github.com/umutbozkurt/django-rest-framework-mongoengine
+[djangorestframework-gis]: https://github.com/djangonauts/django-rest-framework-gis
+[djangorestframework-hstore]: https://github.com/djangonauts/django-rest-framework-hstore
+[drf-compound-fields]: https://github.com/estebistec/drf-compound-fields
+[django-extra-fields]: https://github.com/Hipo/drf-extra-fields
+[djangorestframework-bulk]: https://github.com/miki725/django-rest-framework-bulk
+[drf-nested-routers]: https://github.com/alanjds/drf-nested-routers
+[wq.db.rest]: http://wq.io/docs/about-rest
+[djangorestframework-msgpack]: https://github.com/juanriaza/django-rest-framework-msgpack
+[djangorestframework-camel-case]: https://github.com/vbabiy/djangorestframework-camel-case
+[djangorestframework-csv]: https://github.com/mjumbewu/django-rest-framework-csv
+[drf_ujson]: https://github.com/gizmag/drf-ujson-renderer
+[rest-pandas]: https://github.com/wq/django-rest-pandas
+[djangorestframework-chain]: https://github.com/philipn/django-rest-framework-chain
+[djangorestrelationalhyperlink]: https://github.com/fredkingham/django_rest_model_hyperlink_serializers_project
+[django-rest-swagger]: https://github.com/marcgibbons/django-rest-swagger
+[django-rest-framework-proxy]: https://github.com/eofs/django-rest-framework-proxy
+[gaiarestframework]: https://github.com/AppsFuel/gaiarestframework
+[drf-extensions]: https://github.com/chibisov/drf-extensions
+[ember-django-adapter]: https://github.com/dustinfarris/ember-django-adapter
+[beginners-guide-to-the-django-rest-framework]: http://code.tutsplus.com/tutorials/beginners-guide-to-the-django-rest-framework--cms-19786
+[getting-started-with-django-rest-framework-and-angularjs]: http://blog.kevinastone.com/getting-started-with-django-rest-framework-and-angularjs.html
+[end-to-end-web-app-with-django-rest-framework-angularjs]: http://blog.mourafiq.com/post/55034504632/end-to-end-web-app-with-django-rest-framework
+[start-your-api-django-rest-framework-part-1]: https://godjango.com/41-start-your-api-django-rest-framework-part-1/
+[permissions-authentication-django-rest-framework-part-2]: https://godjango.com/43-permissions-authentication-django-rest-framework-part-2/
+[viewsets-and-routers-django-rest-framework-part-3]: https://godjango.com/45-viewsets-and-routers-django-rest-framework-part-3/
+[django-rest-framework-user-endpoint]: http://richardtier.com/2014/02/25/django-rest-framework-user-endpoint/
+[check-credentials-using-django-rest-framework]: http://richardtier.com/2014/03/06/110/
+[ember-and-django-part 1-video]: http://www.neckbeardrepublic.com/screencasts/ember-and-django-part-1
+[django-rest-framework-part-1-video]: http://www.neckbeardrepublic.com/screencasts/django-rest-framework-part-1
+[pyowa-july-2013-django-rest-framework-video]: http://www.youtube.com/watch?v=e1zrehvxpbo
+[django-rest-framework-and-angularjs-video]: http://www.youtube.com/watch?v=q8frbgtj020
+[web-api-performance-profiling-django-rest-framework]: http://dabapps.com/blog/api-performance-profiling-django-rest-framework/
+[api-development-with-django-and-django-rest-framework]: https://bnotions.com/api-development-with-django-and-django-rest-framework/
+[django-rest-auth]: https://github.com/Tivix/django-rest-auth/
diff --git a/docs/topics/writable-nested-serializers.md b/docs/topics/writable-nested-serializers.md
new file mode 100644
index 00000000..ed614bd2
--- /dev/null
+++ b/docs/topics/writable-nested-serializers.md
@@ -0,0 +1,47 @@
+> To save HTTP requests, it may be convenient to send related documents along with the request.
+>
+> &mdash; [JSON API specification for Ember Data][cite].
+
+# Writable nested serializers
+
+Although flat data structures serve to properly delineate between the individual entities in your service, there are cases where it may be more appropriate or convenient to use nested data structures.
+
+Nested data structures are easy enough to work with if they're read-only - simply nest your serializer classes and you're good to go. However, there are a few more subtleties to using writable nested serializers, due to the dependencies between the various model instances, and the need to save or delete multiple instances in a single action.
+
+## One-to-many data structures
+
+*Example of a **read-only** nested serializer. Nothing complex to worry about here.*
+
+ class ToDoItemSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = ToDoItem
+ fields = ('text', 'is_completed')
+
+ class ToDoListSerializer(serializers.ModelSerializer):
+ items = ToDoItemSerializer(many=True, read_only=True)
+
+ class Meta:
+ model = ToDoList
+ fields = ('title', 'items')
+
+Some example output from our serializer.
+
+ {
+ 'title': 'Leaving party preperations',
+ 'items': {
+ {'text': 'Compile playlist', 'is_completed': True},
+ {'text': 'Send invites', 'is_completed': False},
+ {'text': 'Clean house', 'is_completed': False}
+ }
+ }
+
+Let's take a look at updating our nested one-to-many data structure.
+
+### Validation errors
+
+### Adding and removing items
+
+### Making PATCH requests
+
+
+[cite]: http://jsonapi.org/format/#url-based-json-api