mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-07-29 01:20:02 +03:00
docs updated for generic relationships
This commit is contained in:
parent
b894b3c15d
commit
4ca19d6ec3
|
@ -322,7 +322,7 @@ See the Django documentation on [reverse relationships][reverse-relationships] f
|
|||
|
||||
## Generic relationships
|
||||
|
||||
If you want to serialize a generic foreign key, you need to define a custom field, to determine explicitly how you want serialize the targets of the relationship.
|
||||
If you want to serialize a generic foreign key, you need to define a `GenericRelatedField` with a configuration dictionary as first argument, that describes the representation of each model you possibly want to connect to the generic foreign key.
|
||||
|
||||
For example, given the following model for a tag, which has a generic relationship with other arbitrary models:
|
||||
|
||||
|
@ -357,40 +357,111 @@ And the following two models, which may be have associated tags:
|
|||
text = models.CharField(max_length=1000)
|
||||
tags = GenericRelation(TaggedItem)
|
||||
|
||||
We could define a custom field that could be used to serialize tagged instances, using the type of each instance to determine how it should be serialized.
|
||||
Now we define serializers for each model that may get associated with tags.
|
||||
|
||||
class TaggedObjectRelatedField(serializers.RelatedField):
|
||||
class BookmarkSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
A custom field to use for the `tagged_object` generic relationship.
|
||||
A simple `ModelSerializer` subclass for serializing `Bookmark` objects.
|
||||
"""
|
||||
class Meta:
|
||||
model = Bookmark
|
||||
exclude = ('id', )
|
||||
|
||||
def to_native(self, value):
|
||||
|
||||
class NoteSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
A simple `ModelSerializer` subclass for serializing `Note` objects.
|
||||
"""
|
||||
class Meta:
|
||||
model = Note
|
||||
exclude = ('id', )
|
||||
|
||||
The model serializer for the `Tag` model could look like this:
|
||||
|
||||
class TagSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
A `Tag` serializer with a `GenericRelatedField` mapping all possible
|
||||
models to their respective serializers.
|
||||
"""
|
||||
tagged_object = serializers.GenericRelatedField({
|
||||
Bookmark: BookmarkSerializer(),
|
||||
Note: NoteSerializer()
|
||||
}, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Tag
|
||||
exclude = ('id', )
|
||||
|
||||
The JSON representation of a `Tag` object with `name='django'` and its generic foreign key pointing at a `Bookmark` object with `url='https://www.djangoproject.com/'` would look like this:
|
||||
|
||||
{
|
||||
'tagged_object': {
|
||||
'url': 'https://www.djangoproject.com/'
|
||||
},
|
||||
'tag': 'django'
|
||||
}
|
||||
|
||||
If you want to have your generic foreign key represented as hyperlink, simply use `HyperlinkedRelatedField` objects:
|
||||
|
||||
class TagSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
A `Tag` serializer with a `GenericRelatedField` mapping all possible
|
||||
models to properly set up `HyperlinkedRelatedField`s.
|
||||
"""
|
||||
tagged_object = serializers.GenericRelatedField({
|
||||
Bookmark: serializers.HyperlinkedRelatedField(view_name='bookmark-detail'),
|
||||
Note: serializers.HyperlinkedRelatedField(view_name='note-detail'),
|
||||
}, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Tag
|
||||
exclude = ('id', )
|
||||
|
||||
The JSON representation of the same `Tag` example object could now look something like this:
|
||||
|
||||
{
|
||||
'tagged_object': '/bookmark/1/',
|
||||
'tag': 'django'
|
||||
}
|
||||
|
||||
These examples cover the default behavior of generic foreign key representation. However, you may also want to write to generic foreign key fields through your API.
|
||||
|
||||
By default, the `GenericRelatedField` can be used with `read_only=False` only if you use `HyperlinkedRelatedField` as representation for every model you register with your `GenericRelatedField`.
|
||||
|
||||
This `Tag` serializer is able to write to it's generic foreign key field:
|
||||
|
||||
class TagSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
Serialize tagged objects to a simple textual representation.
|
||||
"""
|
||||
if isinstance(value, Bookmark):
|
||||
return 'Bookmark: ' + value.url
|
||||
elif isinstance(value, Note):
|
||||
return 'Note: ' + value.text
|
||||
raise Exception('Unexpected type of tagged object')
|
||||
|
||||
If you need the target of the relationship to have a nested representation, you can use the required serializers inside the `.to_native()` method:
|
||||
|
||||
def to_native(self, value):
|
||||
A `Tag` serializer with a `GenericRelatedField` mapping all possible
|
||||
models to properly set up `HyperlinkedRelatedField`s.
|
||||
"""
|
||||
Serialize bookmark instances using a bookmark serializer,
|
||||
and note instances using a note serializer.
|
||||
"""
|
||||
if isinstance(value, Bookmark):
|
||||
serializer = BookmarkSerializer(value)
|
||||
elif isinstance(value, Note):
|
||||
serializer = NoteSerializer(value)
|
||||
else:
|
||||
raise Exception('Unexpected type of tagged object')
|
||||
tagged_object = serializers.GenericRelatedField({
|
||||
Bookmark: serializers.HyperlinkedRelatedField(view_name='bookmark-detail'),
|
||||
Note: serializers.HyperlinkedRelatedField(view_name='note-detail'),
|
||||
}, read_only=False)
|
||||
|
||||
return serializer.data
|
||||
class Meta:
|
||||
model = Tag
|
||||
exclude = ('id', )
|
||||
|
||||
Note that reverse generic keys, expressed using the `GenericRelation` field, can be serialized using the regular relational field types, since the type of the target in the relationship is always known.
|
||||
The following operations would create a `Tag` object with it's `tagged_object` property pointing at the `Bookmark` object found at the given detail end point.
|
||||
|
||||
tag_serializer = TagSerializer(data={
|
||||
'tag': 'python'
|
||||
'tagged_object': '/bookmark/1/'
|
||||
})
|
||||
|
||||
tag_serializer.is_valid()
|
||||
tag_serializer.save()
|
||||
|
||||
If you feel that this default behavior doesn't suit your needs, you can subclass `GenericRelatedField` and override its `determine_serializer_for_data` method to implement your own way of decision-making.
|
||||
|
||||
A few things you should note:
|
||||
|
||||
* Although `GenericForeignKey` fields can be set to any model object, the `GenericRelatedField` only handles models explicitly defined in its configuration dictionary.
|
||||
* Reverse generic keys, expressed using the `GenericRelation` field, can be serialized using the regular relational field types, since the type of the target in the relationship is always known.
|
||||
* You can mix `ModelSerializer` and `HyperlinkedRelatedField` in one `GenericRelatedField` configuration dictionary for deserialization purposes. It is considered bad practice though.
|
||||
* If you mix `ModelSerializer` and `HyperlinkedRelatedField` in one `GenericRelatedField` configuration dictionary, the serialization process (PUT/POST) will raise a `ConfigurationError`.
|
||||
|
||||
For more information see [the Django documentation on generic relations][generic-relations].
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user