Rest Framework Generic Relations 
This library implements Django REST Framework serializers to handle generic foreign keys.
Maintenance status
Originally by Lily Foote. now maintained by Craig de Stigter.
I don't intend to make any major changes to this library, but I will attempt to fix reported bugs and keep it up to date with recent versions of Python / Django / DRF.
Requirements
- At least Python 3.8
- A supported version of Django REST Framework (at least 3.12+)
- A supported version of Django (at least 3.2+)
Installation
Install using pip...
pip install rest-framework-generic-relations
Add 'generic_relations' to your INSTALLED_APPS setting.
INSTALLED_APPS = (
...
'generic_relations',
)
API Reference
GenericRelatedField
This field serializes generic foreign keys. For a primer on generic foreign keys, first see: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/
Let's assume a TaggedItem model which has a generic relationship with other arbitrary models:
class TaggedItem(models.Model):
tag_name = models.SlugField()
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
tagged_object = GenericForeignKey('content_type', 'object_id')
And the following two models, which may have associated tags:
class Bookmark(models.Model):
"""
A bookmark consists of a URL, and 0 or more descriptive tags.
"""
url = models.URLField()
tags = GenericRelation(TaggedItem)
class Note(models.Model):
"""
A note consists of some text, and 0 or more descriptive tags.
"""
text = models.CharField(max_length=1000)
tags = GenericRelation(TaggedItem)
Now we define serializers for each model that may get associated with tags.
class BookmarkSerializer(serializers.ModelSerializer):
class Meta:
model = Bookmark
fields = ('url',)
class NoteSerializer(serializers.ModelSerializer):
class Meta:
model = Note
fields = ('text',)
The model serializer for the TaggedItem model could look like this:
from generic_relations.relations import GenericRelatedField
class TagSerializer(serializers.ModelSerializer):
"""
A `TaggedItem` serializer with a `GenericRelatedField` mapping all possible
models to their respective serializers.
"""
tagged_object = GenericRelatedField({
Bookmark: BookmarkSerializer(),
Note: NoteSerializer()
})
class Meta:
model = TaggedItem
fields = ('tag_name', 'tagged_object')
The JSON representation of a TaggedItem 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_name": "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 = GenericRelatedField({
Bookmark: serializers.HyperlinkedRelatedField(
queryset = Bookmark.objects.all(),
view_name='bookmark-detail',
),
Note: serializers.HyperlinkedRelatedField(
queryset = Note.objects.all(),
view_name='note-detail',
),
})
class Meta:
model = TaggedItem
fields = ('tag_name', 'tagged_object')
The JSON representation of the same TaggedItem example object could now look something like this:
{
"tagged_object": "/bookmark/1/",
"tag_name": "django"
}
Writing to generic foreign keys
The above TagSerializer is also writable. By default, a GenericRelatedField iterates over its nested serializers and returns the value of the first serializer that is actually able to perform to_internal_value() without any errors.
Note, that (at the moment) only HyperlinkedRelatedField is able to serialize model objects out of the box.
The following operations would create a TaggedItem object with it's tagged_object property pointing at the Bookmark object found at the given detail end point.
tag_serializer = TagSerializer(data={
'tag_name': '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 get_serializer_for_instance or get_deserializer_for_data respectively to implement your own way of decision-making.
GenericModelSerializer
Sometimes you may want to serialize a single list of different top-level things. For instance, suppose I have an API view that returns what items are on my bookshelf. Let's define some models:
from django.core.validators import MaxValueValidator
class Book(models.Model):
title = models.CharField(max_length=255)
author = models.CharField(max_length=255)
class Bluray(models.Model):
title = models.CharField(max_length=255)
rating = models.PositiveSmallIntegerField(
validators=[MaxValueValidator(5)],
)
Then we could have a serializer for each type of object:
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ('title', 'author')
class BluraySerializer(serializers.ModelSerializer):
class Meta:
model = Bluray
fields = ('title', 'rating')
Now we can create a generic list serializer, which delegates to the above serializers based on the type of model it's serializing:
bookshelf_item_serializer = GenericModelSerializer(
{
Book: BookSerializer(),
Bluray: BluraySerializer(),
},
many=True,
)
Then we can serialize a mixed list of items:
>>> bookshelf_item_serializer.to_representation([
Book.objects.get(title='War and Peace'),
Bluray.objects.get(title='Die Hard'),
Bluray.objects.get(title='Shawshank Redemption'),
Book.objects.get(title='To Kill a Mockingbird'),
])
[
{'title': 'War and Peace', 'author': 'Leo Tolstoy'},
{'title': 'Die Hard', 'rating': 5},
{'title': 'Shawshank Redemption', 'rating': 5},
{'title': 'To Kill a Mockingbird', 'author': 'Harper Lee'}
]
A few things you should note:
- Although
GenericForeignKeyfields can be set to any model object, theGenericRelatedFieldonly handles models explicitly defined in its configuration dictionary. - Reverse generic keys, expressed using the
GenericRelationfield, can be serialized using the regular relational field types, since the type of the target in the relationship is always known. - The order in which you register serializers matters as far as write operations are concerned.
- Unless you provide a custom
get_deserializer_for_data()method, onlyHyperlinkedRelatedFieldprovides write access to generic model relations.
Metadata
Release files for rest-framework-generic-relations 2.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| rest-framework-generic-relations-2.2.0.tar.gz | 11.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| rest_framework_generic_relations-2.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.4 kB
Release files / rest-framework-generic-relations-2.2.0.tar.gz
| Download URL | rest-framework-generic-relations-2.2.0.tar.gz |
|---|---|
| Size | 11.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
17a1f8a129a1c72c8ddc82e9454ea09012f60c332778578aa6a5a3a7135c0a35
|
|
BLAKE2b-256 checksum How to use checksums |
d058d5fda1dfac96a85b1b138bfafd2438c17083110879f814cb3a6c9986297a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.1 CPython/3.9.17
|
Release files / rest_framework_generic_relations-2.2.0-py3-none-any.whl
| Download URL | rest_framework_generic_relations-2.2.0-py3-none-any.whl |
|---|---|
| Size | 11.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fea13c42501cf6ee93c951d38d47d2ea727d29f4348b5d551d763a024cf28660
|
|
BLAKE2b-256 checksum How to use checksums |
ab38ef41228094d2bdc1665f4599ff353ca298c6548968b8eb45e283ad2974bd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.1 CPython/3.9.17
|