Skip to main content

Rest Framework Generic Relations Build Status

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 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.
  • 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, only HyperlinkedRelatedField provides 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)

Source distribution for rest-framework-generic-relations 2.2.0
File Size Uploaded
rest-framework-generic-relations-2.2.0.tar.gz 11.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for rest-framework-generic-relations 2.2.0
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

2.2.0 This release

2 release files

2.1.0

2 release files

2.0.0

2 release files

1.2.2

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page