17
votes

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

3

3 Answers

5
votes

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
3
votes

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
1
votes

The simplest thing that occurs to me is to override get_serializer_class() on your view for POST/PUT to return a modified serializer not specifying the depth parameter and using a PrimaryKeyRelatedField for your languages field.