@@ -271,7 +271,9 @@ Similarly if a nested representation should be a list of items, you should pass
...
@@ -271,7 +271,9 @@ Similarly if a nested representation should be a list of items, you should pass
content = serializers.CharField(max_length=200)
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
created = serializers.DateTimeField()
Validation of nested objects will work the same as before. Errors with nested objects will be nested under the field name of the nested object.
## Writable nested representations
When dealing with nested representations that support deserializing the data, an errors with nested objects will be nested under the field name of the nested object.
@@ -279,89 +281,117 @@ Validation of nested objects will work the same as before. Errors with nested o
...
@@ -279,89 +281,117 @@ Validation of nested objects will work the same as before. Errors with nested o
serializer.errors
serializer.errors
# {'user': {'email': [u'Enter a valid e-mail address.']}, 'created': [u'This field is required.']}
# {'user': {'email': [u'Enter a valid e-mail address.']}, 'created': [u'This field is required.']}
**TODO** Document create and update for nested serializers
Similarly, the `.validated_data` property will include nested data structures.
## Dealing with multiple objects
#### Writing `.create()` methods for nested representations
The `Serializer` class can also handle serializing or deserializing lists of objects.
If you're supporting writable nested representations you'll need to write `.create()` or `.update()` methods that handle saving multiple objects.
#### Serializing multiple objects
The following example demonstrates how you might handle creating a user with a nested profile object.
To serialize a queryset or list of objects instead of a single object instance, you should pass the `many=True` flag when instantiating the serializer. You can then pass a queryset or list of objects to be serialized.
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
queryset = Book.objects.all()
class Meta:
serializer = BookSerializer(queryset, many=True)
model = User
serializer.data
fields = ('username', 'email', 'profile')
# [
# {'id': 0, 'title': 'The electric kool-aid acid test', 'author': 'Tom Wolfe'},
# {'id': 1, 'title': 'If this is a man', 'author': 'Primo Levi'},
#### Writing `.update()` methods for nested representations
To deserialize a list of object data, and create multiple object instances in a single pass, you should also set the `many=True` flag, and pass a list of data to be deserialized.
For updates you'll want to think carefully about how to handle updates to relationships. For example if the data for the relationship is `None`, or not provided, which of the following should occur?
This allows you to write views that create multiple items when a `POST` request is made.
* Set the relationship to `NULL` in the database.
* Delete the associated instance.
* Ignore the data and leave the instance as it is.
* Raise a validation error.
For example:
Here's an example for an `update()` method on our previous `UserSerializer` class.
data = [
def update(self, instance, validated_data):
{'title': 'The bell jar', 'author': 'Sylvia Plath'},
profile_data = validated_data.pop('profile')
{'title': 'For whom the bell tolls', 'author': 'Ernest Hemingway'}
# Unless the application properly enforces that this field is
]
# always set, the follow could raise a `DoesNotExist`, which
serializer = BookSerializer(data=data, many=True)
# would need to be handled.
serializer.is_valid()
profile = instance.profile
# True
serializer.save() # `.save()` will be called on each deserialized instance
The default implementation for multiple object creation is to simply call `.create()` for each item in the list. If you want to customize this behavior, you'll need to customize the `.create()` method on `ListSerializer` class that is used when `many=True` is passed.
profile.is_premium_member = profile_data.get(
'is_premium_member',
profile.is_premium_member
)
profile.has_support_contract = profile_data.get(
'has_support_contract',
profile.has_support_contract
)
profile.save()
For example:
return user
class BookListSerializer(serializers.ListSerializer):
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.
def create(self, validated_data):
books = [Book(**item) for item in validated_data]
return Book.objects.bulk_create(books)
class BookSerializer(serializers.Serializer):
It is possible that a third party package, providing automatic support some kinds of automatic writable nested representations may be released alongside the 3.1 release.
…
class Meta:
list_serializer_class = BookListSerializer
#### Deserializing multiple objects for update
#### Handling saving related instances in model manager classes
**TODO**
An alternative to saving multiple related instances in the serializer is to write custom model manager classes handle creating the correct instances.
You can also deserialize a list of objects as part of a bulk update of multiple existing items.
For example, suppose we wanted to ensure that `User` instances and `Profile` instances are always created together as a pair. We might write a custom manager class that looks something like this:
In this case you need to supply both an existing list or queryset of items, as well as a list of data to update those items with.
This allows you to write views that update or create multiple items when a `PUT` request is made.
serializer.save() # `.save()` will be called on each updated or newly created instance.
return user
By default bulk updates will be limited to updating instances that already exist in the provided queryset.
This manager class now more nicely encapsulates that user instances and profile instances are always created at the same time. Our `.create()` method on the serializer class can now be re-written to use the new manager method.
When performing a bulk update you may want to allow new items to be created, and missing items to be deleted. To do so, pass `allow_add_remove=True` to the serializer.
For more details on this approach see the Django documentation on [model managers](model-managers), and [this blogpost on using model and manger classes](encapsulation-blogpost).
serializer.is_valid()
# True
serializer.save() # `.save()` will be called on updated or newly created instances.
# `.delete()` will be called on any other items in the `queryset`.
Passing `allow_add_remove=True` ensures that any update operations will completely overwrite the existing queryset, rather than simply updating existing objects.
## Dealing with multiple objects
The `Serializer` class can also handle serializing or deserializing lists of objects.
#### Serializing multiple objects
To serialize a queryset or list of objects instead of a single object instance, you should pass the `many=True` flag when instantiating the serializer. You can then pass a queryset or list of objects to be serialized.
queryset = Book.objects.all()
serializer = BookSerializer(queryset, many=True)
serializer.data
# [
# {'id': 0, 'title': 'The electric kool-aid acid test', 'author': 'Tom Wolfe'},
# {'id': 1, 'title': 'If this is a man', 'author': 'Primo Levi'},
The default behavior for deserializing multiple objects is to support multiple object creation, but not support multiple object updates. For more information on how to support or customize either of these cases, see the [ListSerializer](#ListSerializer) documentation below.
## Including extra context
## Including extra context
...
@@ -399,7 +429,7 @@ By default, all the model fields on the class will be mapped to a corresponding
...
@@ -399,7 +429,7 @@ By default, all the model fields on the class will be mapped to a corresponding
Any relationships such as foreign keys on the model will be mapped to `PrimaryKeyRelatedField`. Reverse relationships are not included by default unless explicitly included as described below.
Any relationships such as foreign keys on the model will be mapped to `PrimaryKeyRelatedField`. Reverse relationships are not included by default unless explicitly included as described below.
#### Inspecting the generated `ModelSerializer` class.
#### Inspecting a `ModelSerializer`
Serializer classes generate helpful verbose representation strings, that allow you to fully inspect the state of their fields. This is particularly useful when working with `ModelSerializers` where you want to determine what set of fields and validators are being automatically created for you.
Serializer classes generate helpful verbose representation strings, that allow you to fully inspect the state of their fields. This is particularly useful when working with `ModelSerializers` where you want to determine what set of fields and validators are being automatically created for you.
...
@@ -607,7 +637,7 @@ For example:
...
@@ -607,7 +637,7 @@ For example:
class Meta:
class Meta:
list_serializer_class = CustomListSerializer
list_serializer_class = CustomListSerializer
#### Customizing `.create()` for multiple objects.
#### Customizing multiple create
The default implementation for multiple object creation is to simply call `.create()` for each item in the list. If you want to customize this behavior, you'll need to customize the `.create()` method on `ListSerializer` class that is used when `many=True` is passed.
The default implementation for multiple object creation is to simply call `.create()` for each item in the list. If you want to customize this behavior, you'll need to customize the `.create()` method on `ListSerializer` class that is used when `many=True` is passed.
...
@@ -623,7 +653,7 @@ For example:
...
@@ -623,7 +653,7 @@ For example:
class Meta:
class Meta:
list_serializer_class = BookListSerializer
list_serializer_class = BookListSerializer
#### Customizing `.update()` for multiple objects.
#### Customizing multiple update
By default the `ListSerializer` class does not support multiple updates. This is because the behavior that should be expected for insertions and deletions is ambiguous.
By default the `ListSerializer` class does not support multiple updates. This is because the behavior that should be expected for insertions and deletions is ambiguous.
...
@@ -663,6 +693,8 @@ Here's an example of how you might choose to implement multiple updates:
...
@@ -663,6 +693,8 @@ Here's an example of how you might choose to implement multiple updates:
class Meta:
class Meta:
list_serializer_class = BookListSerializer
list_serializer_class = BookListSerializer
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.
---
---
# BaseSerializer
# BaseSerializer
...
@@ -687,7 +719,7 @@ Because this class provides the same interface as the `Serializer` class, you ca
...
@@ -687,7 +719,7 @@ Because this class provides the same interface as the `Serializer` class, you ca
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.
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.
##### 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:
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:
...
@@ -721,7 +753,7 @@ Or use it to serialize multiple instances:
...
@@ -721,7 +753,7 @@ Or use it to serialize multiple instances:
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.
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.
...
@@ -766,7 +798,7 @@ Here's a complete example of our previous `HighScoreSerializer`, that's been upd
...
@@ -766,7 +798,7 @@ Here's a complete example of our previous `HighScoreSerializer`, that's been upd
def create(self, validated_data):
def create(self, validated_data):
return HighScore.objects.create(**validated_data)
return HighScore.objects.create(**validated_data)
#### Creating new generic serializers with `BaseSerializer`.
#### Creating new base classes
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 `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.
...
@@ -905,6 +937,8 @@ The [django-rest-framework-hstore][django-rest-framework-hstore] package provide
...
@@ -905,6 +937,8 @@ The [django-rest-framework-hstore][django-rest-framework-hstore] package provide