diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md index 8a810edb9..9b6b7dc3a 100644 --- a/docs/api-guide/relations.md +++ b/docs/api-guide/relations.md @@ -238,7 +238,7 @@ Nested relationships can be expressed by using serializers as fields. If the field is used to represent a to-many relationship, you should add the `many=True` flag to the serializer field. -## Example +## Read-only example For example, the following serializer: @@ -256,6 +256,15 @@ For example, the following serializer: Would serialize to a nested representation like this: + >>> album = Album.objects.create(album_name="The Grey Album", artist='Danger Mouse') + >>> Track.objects.create(album=album, order=1, title='Public Service Announcement', duration=0) + + >>> Track.objects.create(album=album, order=2, title='What More Can I Say', duration=0) + + >>> Track.objects.create(album=album, order=3, title='Encore', duration=0) + + >>> serializer = AlbumSerializer(instance=album) + >>> serializer.data { 'album_name': 'The Grey Album', 'artist': 'Danger Mouse', @@ -263,10 +272,52 @@ Would serialize to a nested representation like this: {'order': 1, 'title': 'Public Service Announcement'}, {'order': 2, 'title': 'What More Can I Say'}, {'order': 3, 'title': 'Encore'}, - ... ], } +## Writable nested serializers + +To use writable nested serialization you'll want to declare a nested field on +the serializer class, and write the `create(validated_data)` and/or +`update(instance, validated_data)` methods explicitly. +Note that nested serializers also works for regular serializers. + + class TrackSerializer(serializers.ModelSerializer): + class Meta: + model = Track + fields = ('order', 'title') + + class AlbumSerializer(serializers.ModelSerializer): + tracks = TrackSerializer(many=True) + + class Meta: + model = Album + fields = ('album_name', 'artist', 'tracks') + + def create(self, validated_data): + tracks_data = validated_data.pop('tracks') + album = Album.objects.create(**validated_data) + for track_data in tracks_data: + Track.objects.create(album=album, duration=0, **track_data) + return album + + >>> data = { + 'album_name': 'The Grey Album', + 'artist': 'Danger Mouse', + 'tracks': [ + {'order': 1, 'title': 'Public Service Announcement'}, + {'order': 2, 'title': 'What More Can I Say'}, + {'order': 3, 'title': 'Encore'}, + ], + } + >>> serializer = AlbumSerializer(data=data) + >>> serializer.is_valid() + True + >>> serializer.save() + + >>> + + # Custom relational fields To implement a custom relational field, you should override `RelatedField`, and implement the `.to_representation(self, value)` method. This method takes the target of the field as the `value` argument, and should return the representation that should be used to serialize the target. The `value` argument will typically be a model instance. diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 2f13bd2a4..da275e054 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -296,19 +296,26 @@ Similarly, the `.validated_data` property will include nested data structures. If you're supporting writable nested representations you'll need to write `.create()` or `.update()` methods that handle saving multiple objects. The following example demonstrates how you might handle creating a user with a nested profile object. +Please note that though we're using Django's Models it could be any Python class as well. - class UserSerializer(serializers.ModelSerializer): - profile = ProfileSerializer() - class Meta: - model = User - fields = ('username', 'email', 'profile') + class CommentSerializer(serializers.Serializer): + user = UserSerializer(required=False) # May be an anonymous user. + content = serializers.CharField(max_length=200) + created = serializers.DateTimeField() 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 + # Get or create the user if provided + user = None + user_data = validated_data.pop('user') + if user_data: + user = User.objects.get_or_create(**user_data) + + # Create the comment + comment = Comment.objects.create(user=user, **validated_data) + + return comment + #### Writing `.update()` methods for nested representations @@ -319,9 +326,10 @@ For updates you'll want to think carefully about how to handle updates to relati * Ignore the data and leave the instance as it is. * Raise a validation error. -Here's an example for an `update()` method on our previous `UserSerializer` class. +Here's an example for an `update()` method on our previous `CommentSerializer` class. def update(self, instance, validated_data): + profile_data = validated_data.pop('profile') # Unless the application properly enforces that this field is # always set, the follow could raise a `DoesNotExist`, which