aboutsummaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/api-guide/fields.md49
-rw-r--r--docs/api-guide/serializers.md58
-rw-r--r--docs/api-guide/validators.md2
-rw-r--r--docs/api-guide/viewsets.md3
-rw-r--r--docs/index.md6
-rw-r--r--docs/topics/2.3-announcement.md6
-rw-r--r--docs/topics/3.0-announcement.md14
-rw-r--r--docs/topics/contributing.md1
-rw-r--r--docs/topics/release-notes.md14
-rw-r--r--docs/topics/third-party-resources.md2
-rw-r--r--docs/tutorial/1-serialization.md42
-rw-r--r--docs/tutorial/4-authentication-and-permissions.md4
-rw-r--r--docs/tutorial/5-relationships-and-hyperlinked-apis.md2
-rw-r--r--docs/tutorial/6-viewsets-and-routers.md4
-rw-r--r--docs/tutorial/quickstart.md5
15 files changed, 148 insertions, 64 deletions
diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md
index 391a52e5..aa5cc84e 100644
--- a/docs/api-guide/fields.md
+++ b/docs/api-guide/fields.md
@@ -453,7 +453,7 @@ If you want to create a custom field, you'll need to subclass `Field` and then o
The `.to_representation()` method is called to convert the initial datatype into a primitive, serializable datatype.
-The `to_internal_value()` method is called to restore a primitive datatype into its internal python representation.
+The `to_internal_value()` method is called to restore a primitive datatype into its internal python representation. This method should raise a `serializer.ValidationError` if the data is invalid.
Note that the `WritableField` class that was present in version 2.x no longer exists. You should subclass `Field` and override `to_internal_value()` if the field supports data input.
@@ -498,6 +498,53 @@ As an example, let's create a field that can be used represent the class name of
"""
return obj.__class__.__name__
+#### Raising validation errors
+
+Our `ColorField` class above currently does not perform any data validation.
+To indicate invalid data, we should raise a `serializers.ValidationError`, like so:
+
+ def to_internal_value(self, data):
+ if not isinstance(data, six.text_type):
+ msg = 'Incorrect type. Expected a string, but got %s'
+ raise ValidationError(msg % type(data).__name__)
+
+ if not re.match(r'^rgb\([0-9]+,[0-9]+,[0-9]+\)$', data):
+ raise ValidationError('Incorrect format. Expected `rgb(#,#,#)`.')
+
+ data = data.strip('rgb(').rstrip(')')
+ red, green, blue = [int(col) for col in data.split(',')]
+
+ if any([col > 255 or col < 0 for col in (red, green, blue)]):
+ raise ValidationError('Value out of range. Must be between 0 and 255.')
+
+ return Color(red, green, blue)
+
+The `.fail()` method is a shortcut for raising `ValidationError` that takes a message string from the `error_messages` dictionary. For example:
+
+ default_error_messages = {
+ 'incorrect_type': 'Incorrect type. Expected a string, but got {input_type}',
+ 'incorrect_format': 'Incorrect format. Expected `rgb(#,#,#)`.',
+ 'out_of_range': 'Value out of range. Must be between 0 and 255.'
+ }
+
+ def to_internal_value(self, data):
+ if not isinstance(data, six.text_type):
+ msg = 'Incorrect type. Expected a string, but got %s'
+ self.fail('incorrect_type', input_type=type(data).__name__)
+
+ if not re.match(r'^rgb\([0-9]+,[0-9]+,[0-9]+\)$', data):
+ self.fail('incorrect_format')
+
+ data = data.strip('rgb(').rstrip(')')
+ red, green, blue = [int(col) for col in data.split(',')]
+
+ if any([col > 255 or col < 0 for col in (red, green, blue)]):
+ self.fail('out_of_range')
+
+ return Color(red, green, blue)
+
+This style keeps you error messages more cleanly separated from your code, and should be preferred.
+
# Third party packages
The following third party packages are also available.
diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md
index 0ee80d53..5fe6b4c2 100644
--- a/docs/api-guide/serializers.md
+++ b/docs/api-guide/serializers.md
@@ -96,7 +96,7 @@ If we want to be able to return complete object instances based on the validated
If your object instances correspond to Django models you'll also want to ensure that these methods save the object to the database. For example, if `Comment` was a Django model, the methods might look like this:
def create(self, validated_data):
- return Comment.objcts.create(**validated_data)
+ return Comment.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.email = validated_data.get('email', instance.email)
@@ -104,7 +104,7 @@ If your object instances correspond to Django models you'll also want to ensure
instance.created = validated_data.get('created', instance.created)
instance.save()
return instance
-
+
Now when deserializing data, we can call `.save()` to return an object instance, based on the validated data.
comment = serializer.save()
@@ -113,7 +113,7 @@ Calling `.save()` will either create a new instance, or update an existing insta
# .save() will create a new instance.
serializer = CommentSerializer(data=data)
-
+
# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)
@@ -140,7 +140,7 @@ For example:
class ContactForm(serializers.Serializer):
email = serializers.EmailField()
message = serializers.CharField()
-
+
def save(self):
email = self.validated_data['email']
message = self.validated_data['message']
@@ -230,7 +230,7 @@ Serializer classes can also include reusable validators that are applied to the
name = serializers.CharField()
room_number = serializers.IntegerField(choices=[101, 102, 103, 201])
date = serializers.DateField()
-
+
class Meta:
# Each room only has one event per day.
validators = UniqueTogetherValidator(
@@ -326,9 +326,9 @@ Here's an example for an `update()` method on our previous `UserSerializer` clas
# would need to be handled.
profile = instance.profile
- user.username = validated_data.get('username', instance.username)
- user.email = validated_data.get('email', instance.email)
- user.save()
+ instance.username = validated_data.get('username', instance.username)
+ instance.email = validated_data.get('email', instance.email)
+ instance.save()
profile.is_premium_member = profile_data.get(
'is_premium_member',
@@ -340,7 +340,7 @@ Here's an example for an `update()` method on our previous `UserSerializer` clas
)
profile.save()
- return user
+ return instance
Because the behavior of nested creates and updates can be ambiguous, and may require complex dependancies between related models, REST framework 3 requires you to always write these methods explicitly. The default `ModelSerializer` `.create()` and `.update()` methods do not include support for writable nested representations.
@@ -448,7 +448,7 @@ To do so, open the Django shell, using `python manage.py shell`, then import the
id = IntegerField(label='ID', read_only=True)
name = CharField(allow_blank=True, max_length=100, required=False)
owner = PrimaryKeyRelatedField(queryset=User.objects.all())
-
+
## Specifying which fields should be included
If you only want a subset of the default fields to be used in a model serializer, you can do so using `fields` or `exclude` options, just as you would with a `ModelForm`.
@@ -505,6 +505,21 @@ This option should be a list or tuple of field names, and is declared as follows
Model fields which have `editable=False` set, and `AutoField` fields will be set to read-only by default, and do not need to be added to the `read_only_fields` option.
+---
+
+**Note**: There is a special-case where a read-only field is part of a `unique_together` constraint at the model level. In this case the field is required by the serializer class in order to validate the constraint, but should also not be editable by the user.
+
+The right way to deal with this is to specify the field explicitly on the serializer, providing both the `read_only=True` and `default=…` keyword arguments.
+
+One example of this is a read-only relation to the currently authenticated `User` which is `unique_together` with another identifier. In this case you would declare the user field like so:
+
+ user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
+
+Please review the [Validators Documentation](/api-guide/validators/) for details on the [UniqueTogetherValidator](/api-guide/validators/#uniquetogethervalidator) and [CurrentUserDefault](/api-guide/validators/#currentuserdefault) classes.
+
+---
+
+
## Specifying additional keyword arguments for fields.
There is also a shortcut allowing you to specify arbitrary additional keyword arguments on fields, using the `extra_kwargs` option. Similarly to `read_only_fields` this means you do not need to explicitly declare the field on the serializer.
@@ -516,7 +531,7 @@ This option is a dictionary, mapping field names to a dictionary of keyword argu
model = User
fields = ('email', 'username', 'password')
extra_kwargs = {'password': {'write_only': True}}
-
+
def create(self, validated_data):
user = User(
email=validated_data['email'],
@@ -567,13 +582,13 @@ There needs to be a way of determining which views should be used for hyperlinki
By default hyperlinks are expected to correspond to a view name that matches the style `'{model_name}-detail'`, and looks up the instance by a `pk` keyword argument.
-You can override a URL field view name and lookup field by using either, or both of, the `view_name` and `lookup_field` options in the `extra_field_kwargs` setting, like so:
+You can override a URL field view name and lookup field by using either, or both of, the `view_name` and `lookup_field` options in the `extra_kwargs` setting, like so:
class AccountSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Account
fields = ('account_url', 'account_name', 'users', 'created')
- extra_field_kwargs = {
+ extra_kwargs = {
'url': {'view_name': 'accounts', 'lookup_field': 'account_name'}
'users': {'lookup_field': 'username'}
}
@@ -656,7 +671,7 @@ To support multiple updates you'll need to do so explicitly. When writing your m
* How do you determine which instance should be updated for each item in the list of data?
* How should insertions be handled? Are they invalid, or do they create new objects?
* How should removals be handled? Do they imply object deletion, or removing a relationship? Should they be silently ignored, or are they invalid?
-* How should ordering be handled? Does changing the position of two items imply any state change or is it ignored?
+* How should ordering be handled? Does changing the position of two items imply any state change or is it ignored?
Here's an example of how you might choose to implement multiple updates:
@@ -689,6 +704,21 @@ Here's an example of how you might choose to implement multiple updates:
It is possible that a third party package may be included alongside the 3.1 release that provides some automatic support for multiple update operations, similar to the `allow_add_remove` behavior that was present in REST framework 2.
+#### Customizing ListSerializer initialization
+
+When a serializer with `many=True` is instantiated, we need to determine which arguments and keyword arguments should be passed to the `.__init__()` method for both the child `Serializer` class, and for the parent `ListSerializer` class.
+
+The default implementation is to pass all arguments to both classes, except for `validators`, and any custom keyword arguments, both of which are assumed to be intended for the child serializer class.
+
+Occasionally you might need to explicitly specify how the child and parent classes should be instantiated when `many=True` is passed. You can do so by using the `many_init` class method.
+
+ @classmethod
+ def many_init(cls, *args, **kwargs):
+ # Instantiate the child serializer.
+ kwargs['child'] = cls()
+ # Instantiate the parent list serializer.
+ return CustomListSerializer(*args, **kwargs)
+
---
# BaseSerializer
diff --git a/docs/api-guide/validators.md b/docs/api-guide/validators.md
index f087e191..8f5a8929 100644
--- a/docs/api-guide/validators.md
+++ b/docs/api-guide/validators.md
@@ -1,4 +1,4 @@
-<a class="github" href="validators.py"></a>
+source: validators.py
---
diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md
index 28186c64..3e37cef8 100644
--- a/docs/api-guide/viewsets.md
+++ b/docs/api-guide/viewsets.md
@@ -201,6 +201,8 @@ Note that you can use any of the standard attributes or method overrides provide
def get_queryset(self):
return self.request.user.accounts.all()
+Note however that upon removal of the `queryset` property from your `ViewSet`, any associated [router][routers] will be unable to derive the base_name of your Model automatically, and so you you will have to specify the `base_name` kwarg as part of your [router registration][routers].
+
Also note that although this class provides the complete set of create/list/retrieve/update/destroy actions by default, you can restrict the available operations by using the standard permission classes.
## ReadOnlyModelViewSet
@@ -243,3 +245,4 @@ To create a base viewset class that provides `create`, `list` and `retrieve` ope
By creating your own base `ViewSet` classes, you can provide common behavior that can be reused in multiple viewsets across your API.
[cite]: http://guides.rubyonrails.org/routing.html
+[routers]: routers.md
diff --git a/docs/index.md b/docs/index.md
index 7631be1e..52e42fc9 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -4,7 +4,7 @@
<a href="https://twitter.com/share" class="twitter-share-button" data-url="django-rest-framework.org" data-text="Checking out the totally awesome Django REST framework! http://www.django-rest-framework.org" data-count="none"></a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="http://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
-<img src="https://secure.travis-ci.org/tomchristie/django-rest-framework.png?branch=master" class="travis-build-image">
+<img src="https://secure.travis-ci.org/tomchristie/django-rest-framework.svg?branch=master" class="travis-build-image">
</p>
---
@@ -33,7 +33,7 @@ Django REST framework is a powerful and flexible toolkit that makes it easy to b
Some reasons you might want to use REST framework:
-* The [Web browseable API][sandbox] is a huge usability win for your developers.
+* The [Web browsable API][sandbox] is a huge usability win for your developers.
* [Authentication policies][authentication] including [OAuth1a][oauth1-section] and [OAuth2][oauth2-section] out of the box.
* [Serialization][serializers] that supports both [ORM][modelserializer-section] and [non-ORM][serializer-section] data sources.
* Customizable all the way down - just use [regular function-based views][functionview-section] if you don't need the [more][generic-views] [powerful][viewsets] [features][routers].
@@ -134,7 +134,7 @@ Here's our project's root `urls.py` module:
router.register(r'users', UserViewSet)
# Wire up our API using automatic URL routing.
- # Additionally, we include login URLs for the browseable API.
+ # 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'))
diff --git a/docs/topics/2.3-announcement.md b/docs/topics/2.3-announcement.md
index 9c9f3e9f..66e46865 100644
--- a/docs/topics/2.3-announcement.md
+++ b/docs/topics/2.3-announcement.md
@@ -35,7 +35,7 @@ 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.
+ # 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'))
@@ -207,9 +207,9 @@ The old-style signature will continue to function but will raise a `PendingDepre
## 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.
diff --git a/docs/topics/3.0-announcement.md b/docs/topics/3.0-announcement.md
index 281e33bd..adbe370c 100644
--- a/docs/topics/3.0-announcement.md
+++ b/docs/topics/3.0-announcement.md
@@ -1,8 +1,8 @@
-# REST framework 3.0
+# 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.
+**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.
@@ -557,22 +557,22 @@ 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_none`, `allow_blank` and `default` arguments.
+#### 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_none` and `allow_blank` arguments.
+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_none=True`: `None` is a valid input.
+* `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_none=True` or `allow_blank=True` if required.
+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.
@@ -931,6 +931,7 @@ The default JSON renderer will return float objects for un-coerced `Decimal` ins
* 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.
---
@@ -952,3 +953,4 @@ You can follow development on the GitHub site, where we use [milestones to indic
[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/contributing.md b/docs/topics/contributing.md
index 99f4fc3c..c9626ebf 100644
--- a/docs/topics/contributing.md
+++ b/docs/topics/contributing.md
@@ -62,7 +62,6 @@ To run the tests, clone the repository, and then:
virtualenv env
source env/bin/activate
pip install -r requirements.txt
- pip install -r requirements-test.txt
# Run the tests
./runtests.py
diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md
index 53187589..550fdf75 100644
--- a/docs/topics/release-notes.md
+++ b/docs/topics/release-notes.md
@@ -38,6 +38,16 @@ You can determine your currently installed version using `pip freeze`:
---
+## 3.0.x series
+
+### 3.0.0
+
+**Date**: 1st December 2014
+
+For full details see the [3.0 release announcement](3.0-announcement.md).
+
+---
+
## 2.4.x series
### 2.4.4
@@ -121,7 +131,7 @@ You can determine your currently installed version using `pip freeze`:
* Add `UnicodeYAMLRenderer` that extends `YAMLRenderer` with unicode.
* Fix `parse_header` argument convertion.
* Fix mediatype detection under Python 3.
-* Web browseable API now offers blank option on dropdown when the field is not required.
+* Web browsable API now offers blank option on dropdown when the field is not required.
* `APIException` representation improved for logging purposes.
* Allow source="*" within nested serializers.
* Better support for custom oauth2 provider backends.
@@ -200,7 +210,7 @@ You can determine your currently installed version using `pip freeze`:
* Added `MAX_PAGINATE_BY` setting and `max_paginate_by` generic view attribute.
* Added `cache` attribute to throttles to allow overriding of default cache.
* 'Raw data' tab in browsable API now contains pre-populated data.
-* 'Raw data' and 'HTML form' tab preference in browseable API now saved between page views.
+* 'Raw data' and 'HTML form' tab preference in browsable API now saved between page views.
* Bugfix: `required=True` argument fixed for boolean serializer fields.
* Bugfix: `client.force_authenticate(None)` should also clear session info if it exists.
* Bugfix: Client sending empty string instead of file now clears `FileField`.
diff --git a/docs/topics/third-party-resources.md b/docs/topics/third-party-resources.md
index efa0b91f..0358d614 100644
--- a/docs/topics/third-party-resources.md
+++ b/docs/topics/third-party-resources.md
@@ -93,7 +93,7 @@ The cookiecutter template includes a `runtests.py` which uses the `pytest` packa
Before running, you'll need to install a couple test requirements.
- $ pip install -r requirements-test.txt
+ $ pip install -r requirements.txt
Once requirements installed, you can run `runtests.py`.
diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md
index 538b0d93..dea43cc0 100644
--- a/docs/tutorial/1-serialization.md
+++ b/docs/tutorial/1-serialization.md
@@ -16,7 +16,6 @@ The tutorial is fairly in-depth, so you should probably get a cookie and a cup o
Before we do anything else we'll create a new virtual environment, using [virtualenv]. This will make sure our package configuration is kept nicely isolated from any other projects we're working on.
- :::bash
virtualenv env
source env/bin/activate
@@ -75,12 +74,8 @@ For the purposes of this tutorial we're going to start by creating a simple `Sni
title = models.CharField(max_length=100, blank=True, default='')
code = models.TextField()
linenos = models.BooleanField(default=False)
- language = models.CharField(choices=LANGUAGE_CHOICES,
- default='python',
- max_length=100)
- style = models.CharField(choices=STYLE_CHOICES,
- default='friendly',
- max_length=100)
+ language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
+ style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)
class Meta:
ordering = ('created',)
@@ -101,30 +96,27 @@ The first thing we need to get started on our Web API is to provide a way of ser
class SnippetSerializer(serializers.Serializer):
pk = serializers.IntegerField(read_only=True)
- title = serializers.CharField(required=False,
- max_length=100)
+ title = serializers.CharField(required=False, allow_blank=True, max_length=100)
code = serializers.CharField(style={'type': 'textarea'})
linenos = serializers.BooleanField(required=False)
- language = serializers.ChoiceField(choices=LANGUAGE_CHOICES,
- default='python')
- style = serializers.ChoiceField(choices=STYLE_CHOICES,
- default='friendly')
+ language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')
+ style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')
- def create(self, validated_attrs):
+ def create(self, validated_data):
"""
Create and return a new `Snippet` instance, given the validated data.
"""
- return Snippet.objects.create(**validated_attrs)
+ return Snippet.objects.create(**validated_data)
- def update(self, instance, validated_attrs):
+ def update(self, instance, validated_data):
"""
Update and return an existing `Snippet` instance, given the validated data.
"""
- instance.title = validated_attrs.get('title', instance.title)
- instance.code = validated_attrs.get('code', instance.code)
- instance.linenos = validated_attrs.get('linenos', instance.linenos)
- instance.language = validated_attrs.get('language', instance.language)
- instance.style = validated_attrs.get('style', instance.style)
+ 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
@@ -181,7 +173,9 @@ Deserialization is similar. First we parse a stream into Python native datatype
serializer = SnippetSerializer(data=data)
serializer.is_valid()
# True
- serializer.object
+ serializer.validated_data
+ # OrderedDict([('title', ''), ('code', 'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])
+ serializer.save()
# <Snippet: Snippet object>
Notice how similar the API is to working with forms. The similarity should become even more apparent when we start writing views that use our serializer.
@@ -210,7 +204,7 @@ One nice property that serializers have is that you can inspect all the fields i
>>> from snippets.serializers import SnippetSerializer
>>> serializer = SnippetSerializer()
- >>> print repr(serializer) # In python 3 use `print(repr(serializer))`
+ >>> print(repr(serializer))
SnippetSerializer():
id = IntegerField(label='ID', read_only=True)
title = CharField(allow_blank=True, max_length=100, required=False)
@@ -301,7 +295,7 @@ We'll also need a view which corresponds to an individual snippet, and can be us
Finally we need to wire these views up. Create the `snippets/urls.py` file:
- from django.conf.urls import patterns, url
+ from django.conf.urls import url
from snippets import views
urlpatterns = [
diff --git a/docs/tutorial/4-authentication-and-permissions.md b/docs/tutorial/4-authentication-and-permissions.md
index 15d93a62..a6d27bf7 100644
--- a/docs/tutorial/4-authentication-and-permissions.md
+++ b/docs/tutorial/4-authentication-and-permissions.md
@@ -43,7 +43,7 @@ And now we can add a `.save()` method to our model class:
When that's all done we'll need to update our database tables.
Normally we'd create a database migration in order to do that, but for the purposes of this tutorial, let's just delete the database and start again.
- rm tmp.db
+ rm -f tmp.db db.sqlite3
rm -r snippets/migrations
python manage.py makemigrations snippets
python manage.py migrate
@@ -59,7 +59,7 @@ Now that we've got some users to work with, we'd better add representations of t
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
- snippets = serializers.PrimaryKeyRelatedField(many=True)
+ snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())
class Meta:
model = User
diff --git a/docs/tutorial/5-relationships-and-hyperlinked-apis.md b/docs/tutorial/5-relationships-and-hyperlinked-apis.md
index 50552616..58422929 100644
--- a/docs/tutorial/5-relationships-and-hyperlinked-apis.md
+++ b/docs/tutorial/5-relationships-and-hyperlinked-apis.md
@@ -44,7 +44,7 @@ Instead of using a concrete generic view, we'll use the base class for represent
As usual we need to add the new views that we've created in to our URLconf.
We'll add a url pattern for our new API root in `snippets/urls.py`:
- url(r'^$', 'api_root'),
+ url(r'^$', views.api_root),
And then add a url pattern for the snippet highlights:
diff --git a/docs/tutorial/6-viewsets-and-routers.md b/docs/tutorial/6-viewsets-and-routers.md
index 3fad509a..816e9da6 100644
--- a/docs/tutorial/6-viewsets-and-routers.md
+++ b/docs/tutorial/6-viewsets-and-routers.md
@@ -112,7 +112,7 @@ Here's our re-wired `urls.py` file.
router.register(r'users', views.UserViewSet)
# The API URLs are now determined automatically by the router.
- # Additionally, we include the login URLs for the browseable API.
+ # Additionally, we include the login URLs for the browsable API.
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
@@ -130,7 +130,7 @@ That doesn't mean it's always the right approach to take. There's a similar set
## Reviewing our work
-With an incredibly small amount of code, we've now got a complete pastebin Web API, which is fully web browseable, and comes complete with authentication, per-object permissions, and multiple renderer formats.
+With an incredibly small amount of code, we've now got a complete pastebin Web API, which is fully web browsable, and comes complete with authentication, per-object permissions, and multiple renderer formats.
We've walked through each step of the design process, and seen how if we need to customize anything we can gradually work our way down to simply using regular Django views.
diff --git a/docs/tutorial/quickstart.md b/docs/tutorial/quickstart.md
index 43220ce8..d6989d56 100644
--- a/docs/tutorial/quickstart.md
+++ b/docs/tutorial/quickstart.md
@@ -19,10 +19,9 @@ Create a new Django project named `tutorial`, then start a new app called `quick
pip install djangorestframework
# Set up a new project with a single application
- django-admin.py startproject tutorial
+ django-admin.py startproject tutorial .
cd tutorial
django-admin.py startapp quickstart
- cd ..
Now sync your database for the first time:
@@ -100,7 +99,7 @@ Okay, now let's wire up the API URLs. On to `tutorial/urls.py`...
router.register(r'groups', views.GroupViewSet)
# Wire up our API using automatic URL routing.
- # Additionally, we include login URLs for the browseable API.
+ # 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'))