docs updated for generic relationships

This commit is contained in:
lukasbuenger 2013-04-01 19:30:01 +02:00
parent b894b3c15d
commit 4ca19d6ec3

View File

@ -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].