Skip to content

Add Serializer per action to ViewSets #4632

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
dyve opened this issue Oct 27, 2016 · 7 comments
Closed

Add Serializer per action to ViewSets #4632

dyve opened this issue Oct 27, 2016 · 7 comments

Comments

@dyve
Copy link

dyve commented Oct 27, 2016

A common use case is to list objects first, and then retrieve details.

The current implementation of ViewSet is to use one Serializer for every action (list, retrieve, etc).

This means listing and then retrieving yields no additional data.

A good solution would be to allow a default Serializer (using the serializer_class attribute of a ViewSet), and an optional dict of action to Serializer, as described in this StackOverflow answer:
http://stackoverflow.com/a/22922156/117831

class MultiSerializerViewSetMixin(object):
    def get_serializer_class(self):
        """
        Look for serializer class in self.serializer_action_classes, which
        should be a dict mapping action name (key) to serializer class (value),
        i.e.:

        class MyViewSet(MultiSerializerViewSetMixin, ViewSet):
            serializer_class = MyDefaultSerializer
            serializer_action_classes = {
               'list': MyListSerializer,
               'my_action': MyActionSerializer,
            }

            @action
            def my_action:
                ...

        If there's no entry for that action then just fallback to the regular
        get_serializer_class lookup: self.serializer_class, DefaultSerializer.

        Thanks gonz: http://stackoverflow.com/a/22922156/11440

        """
        try:
            return self.serializer_action_classes[self.action]
        except (KeyError, AttributeError):
            return super(MultiSerializerViewSetMixin, self).get_serializer_class()

Note: I am not the author of this answer and code, this is just an answer to a problem I have with DRF, and I think it's worth looking at this as an enhancement.

@dyve
Copy link
Author

dyve commented Oct 27, 2016

This might relate to #2062, and even be a partial solution for that issue.

@ekzobrain
Copy link

ekzobrain commented Oct 27, 2016

Yes, the problem is very actual. Currently I have to create a new serializer per any viewset action, because serializer fields most of the time differ for each method:

  1. List - a short summary of fields to display in a table, most often with nested relations
  2. Retrieve - get all model fields, most often with nested relations
  3. Create - all models fields, but without nested relations, because when we pass model data to api from a form - we most often pass relations as primary keys, retrieved from "select" elements.
  4. Update - mostly the same as create.

And then configure an appropriate serializer to be used for certain viewset actions. Suggested approach would make it easier than now, but I would advise a little bit different way:
We should try to create just one Serializer and one Viewset per Model. So Serializer should be more flexible with "fields" configuration out of the box. This can be achieved rof example this way:

Allow to configure several lists of fields in the serilizer. For example:

class MyModelSerializer(serializers.ModelSerializer):
    class Meta:
        list_fields: [...],
        retrieve_fields: [...],
        create_fields: [...]
        fields: [...] # default fallback

Fields here are specified as "viewset action"_fields. So appropriate fields will be invoked for viewset actions.
Allow to configure several Serializer fields per one Model field. For example (asume that "relation" is a gjango model foreign key field):

class MyModelSerializer(serializers.ModelSerializer):
    relation_obj = RelationSerializer(model_field='relation', action='list')

    class Meta:
        list_fields: ['relation_obj', 'relation'],
        retrieve_fields: ['relation', ...],
        create_fields: ['relation', ...]
        fields: [...] # default fallback

In this example list action will return "relation" primary key and the whole "relation_obj" object, as specified in RelationSerializer "list_fields" meta property.

@dyve
Copy link
Author

dyve commented Oct 28, 2016

@soulhunter1987 That solution is a bit more complex than what I'd like to see (and suggested).

@tomchristie
Copy link
Member

I'd like to see someone tackle this as a third party mixin class rather than bringing in anything else to core right now.

@gregschmit
Copy link
Contributor

gregschmit commented Jul 28, 2019

Hey @tomchristie, I wrote something that I think fixes this (drf-action-serializer) but rather than making a new ViewSet, I created a ModelActionSerializer that inspects the view action and renders different fields based on the action, and defaults to the normal fields/exclude/extra_kwargs. It accepts a property in Meta called action_fields. If you want to see how it works, I explain it in more detail here: https://stackoverflow.com/questions/37061718/django-rest-framework-hide-specific-fields-in-list-display/57244475#57244475

@gregschmit
Copy link
Contributor

@tomchristie I am considering opening a pull request to pull this functionality into the ModelSerializer since it doesn't break anything and is really a simple change in two methods of that serializer. If I do that and write tests, would you consider pulling it in?

@devmgod
Copy link

devmgod commented Jun 10, 2022

IMO, I'd like to show Data structure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants