17

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

5

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
Sign up to request clarification or add additional context in comments.

1 Comment

While this has worked fine before, fails on the first except for the documentation feature on DRF 3.6.3 (django-rest-framework.org/topics/documenting-your-api )
3

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 Comment

You may run in to issues as you scale using this approach. Meta is a class level variable, shared by all instances. As you toggle the depth while trying to serialize one object, you mess with ALL instances.
1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.