Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django REST Framework and generic relations

Problem

I have a model with the following standard generic foreign key fields:

content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
event_object = generic.GenericForeignKey('content_type', 'object_id')

According to REST framework's docs, I can do the following to serialise this correctly:

class WhateverSerializer(serializers.ModelSerializer):
    event_object = serializers.RelatedField(source='event_object')

This works fine, however in two other related situations, I can't get things working:

  1. I would like to use HyperlinkedRelatedField. This field requires the view_name argument, something I can't declare since the view name varies with the related model. I solved this by using SerializerMethodField, instantiating a HyperlinkedIdentityField at runtime and returning its field_to_native method (see snippet below). This does not feel very elegant.
  2. I would like to nest the related object directly in the serialisation by saying event_object = SoAndSoSerializer(source='event_object'). The only solution I can see is to walk every *Serializer I have defined and check which has the correct model, then use that. Again, this does not feel very elegant.

Questions

is HyperlinkRelatedField meant to work across a generic relationship? Am I just making a mistake? Is there an obvious solution to picking the right *Serializer that I'm missing?

Code Snippet

The inelegant solution mentioned in bullet point 1 above:

class WhateverSerializer(DefaultSerializer):

    event_object_url = serializers.SerializerMethodField('get_related_object_url')
    # ...

    def get_related_object_url(self, obj):
        obj = obj.event_object
        default_view_name = '%(model_name)s-detail'
        format_kwargs = {
            'app_label': obj._meta.app_label,
            'model_name': obj._meta.object_name.lower()
        }
        view_name = default_view_name % format_kwargs
        s = serializers.HyperlinkedIdentityField(source=obj, view_name=view_name)
        s.initialize(self, None)
        return s.field_to_native(obj, None)
like image 531
Ben Graham Avatar asked Jan 21 '13 23:01

Ben Graham


People also ask

Is Django different from Django REST framework?

Django is the web development framework in python whereas the Django Rest Framework is the library used in Django to build Rest APIs. Django Rest Framework is especially designed to make the CRUD operations easier to design in Django. Django Rest Framework makes it easy to use your Django Server as an REST API.

What is difference between APIView and Viewset?

APIView allow us to define functions that match standard HTTP methods like GET, POST, PUT, PATCH, etc. Viewsets allow us to define functions that match to common API object actions like : LIST, CREATE, RETRIEVE, UPDATE, etc.

Can I use Django and Django REST framework together?

Django Rest Framework makes it easy to use your Django Server as an REST API. REST stands for "representational state transfer" and API stands for application programming interface.


1 Answers

Your right, REST framework doesn't support those use cases, and its not obvious to me what the design would look like if it did. You'd probably need an implicit registry of model->view (for the hyperlinked case) and model-> serializer (for the nested case) which I don't think I'd be very keen on.

The simplest way to do what you need is probably to subclass ManyRelatedField and create a custom field type, overriding to_native(self, obj) to serialize each object in the set exactly the way you want it.

like image 92
Tom Christie Avatar answered Nov 15 '22 23:11

Tom Christie