Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django rest framework model serializers - read nested, write flat

I have a situation where my client is attempting to write a representation that includes a list of fk's

{
languages: [1]
last_name: "Beecher"
settings: 1
state: "NY"
}

But when reading it in, I'd like to have a nested representation to cut back on roundtrips

{
languages: [{id:1, created:2013-07-21T01:38:33.569Z, modified:2013-07-21T01:38:33.569Z, language:testing}]
last_name: "Beecher"
settings: {
created: "2013-07-20T22:04:17.998Z"
email_blog: false
email_booking_accepted_denied: false
email_booking_request: false
email_friend_joined: false
email_groups_added_network: false
email_new_review: false
email_news: false
email_upcoming_booking_remind: false
id: 1
mobile_booking_accepted_denied: false
mobile_booking_request: false
mobile_friend_joined: false
mobile_groups_added_network: false
mobile_new_review: false
mobile_upcoming_booking_remind: false
modified: "2013-07-20T22:04:18.000Z"
user: 1
}
state: "NY"
}

Reading is no problem using a model serializer and depth=1 - but attempting to write gives an error "ValueError('instance should be a queryset or other iterable with many=True')" When attempting to check a many related field for iter

Conversely, turning off depth makes writing work just as I'd like, but reading is no good.

Is there something I'm totally missing here? It seems like it should be a simple change, but I can only get one or the other working

like image 604
gone Avatar asked Jul 21 '13 06:07

gone


People also ask

What is Serializers in Django REST framework?

Serializers in Django REST Framework are responsible for converting objects into data types understandable by javascript and front-end frameworks. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.

How do you pass extra context data to Serializers in Django REST framework?

In function based views we can pass extra context to serializer with "context" parameter with a dictionary. To access the extra context data inside the serializer we can simply access it with "self. context". From example, to get "exclude_email_list" we just used code 'exclude_email_list = self.

What is the use of nested serializer?

In general, I use nested serializers when I want an interface where model B is always read (or created) together with model A. An example of this would be a Pizza model with a many-to-many relationship to Topping .

What is the difference between ModelSerializer and HyperlinkedModelSerializer?

The HyperlinkedModelSerializer class is similar to the ModelSerializer class except that it uses hyperlinks to represent relationships, rather than primary keys. By default the serializer will include a url field instead of a primary key field.


2 Answers

Thanks to previous posts, I went with a similar solution based off get_serializer_class for this.

I also wanted to be able to change the serializer class depending on method.

First, I added an attribute to the view class with a dictionary mapping request methods to serializer classes.

 serializer_classes = {
     'GET': NestedSerializer,
     'POST': FlatSerializer
 }

Then, I defined a mixin to use where I want this behavior.

class SwappableSerializerMixin(object):
    def get_serializer_class(self):
        try:
            return self.serializer_classes[self.request.method]
        except AttributeError:
            logger.debug('%(cls)s does not have the required serializer_classes'
                         'property' % {'cls': self.__class__.__name__})
            raise AttributeError
        except KeyError:
            logger.debug('request method %(method)s is not listed'
                         ' in %(cls)s serializer_classes' %
                         {'cls': self.__class__.__name__,
                          'method': self.request.method})
            # required if you don't include all the methods (option, etc) in your serializer_class
            return super(SwappableSerializerMixin, self).get_serializer_class() es
like image 196
kevins Avatar answered Oct 16 '22 07:10

kevins


I had the same problem and it looks like quite a few others have it too. Carlton Gibson answer actually lead me to my hacky solution. I ended up using a ModelSerializer with the depth set and created the following mixin to use in the views.

class ReadNestedWriteFlatMixin(object):
    """
    Mixin that sets the depth of the serializer to 0 (flat) for writing operations.
    For all other operations it keeps the depth specified in the serializer_class
    """
    def get_serializer_class(self, *args, **kwargs):
        serializer_class = super(ReadNestedWriteFlatMixin, self).get_serializer_class(*args, **kwargs)
        if self.request.method in ['PATCH', 'POST', 'PUT']:
            serializer_class.Meta.depth = 0
        return serializer_class
like image 20
niklasae Avatar answered Oct 16 '22 08:10

niklasae