Maverik Minett
Maverik Minett

Reputation: 2452

Django Serializer - Decide which fields are serialized at runtime

I am using DRF and have a model which is serialized. There are times when I want to include all the fields in the data that is sent to the api end point, and times when I want just a sub-set of the properties (for instance list view vs edit view). Is it possible to declare all the fields in one serializer, and then specify only a subset when calling the serializer.

Here is an example of what I would like to do:

queryset = Foo.objects.filter(active=True)
FooSerializer(queryset, many=True, fields=["id", "title"])

I could then use this output to populate a the options of an HTML select element.

Meanwhile FooSerializer looks like this: class

FooSerializer(serializers.ModelSerializer):

  id = serializers.ReadOnlyField()


  class Meta:
    model = ProgressNoteCustomType

    fields = ( 'id', 'title', 'modified', 'active', 'user' )

    read_only_fields = ['id']

While I could write another serializer, that just has the id and title field in the Meta definition, it's not DRY.

Then I have another scenario where I want to display a list view where the user can click on an item and edit it - here I want to display just title, modified, and active. Writing something like FooSerializer(queryset, many=True, fields=["id", "title", "active"]) seems like the appropriate solution, but is not valid.

I really want to avoid having 3 different serializes for these 3 different scenarios ( where the regular FooSerializer(instance) is the edit/view default that returns all the fields defined in the serializer )

Upvotes: 0

Views: 1865

Answers (1)

Ykh
Ykh

Reputation: 7717

class DynamicFieldsModelSerializer(serializers.ModelSerializer):
    """
    A ModelSerializer that takes an additional `fields` argument that
    controls which fields should be displayed.
    """

    def __init__(self, *args, **kwargs):
        # Don't pass the 'fields' arg up to the superclass
        fields = kwargs.pop('fields', None)
        exclude = kwargs.pop('exclude', None)

        # Instantiate the superclass normally
        super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)

        if fields is not None:
            # Drop any fields that are not specified in the `fields` argument.
            allowed = set(fields)
            existing = set(self.fields.keys())
            for field_name in existing - allowed:
                self.fields.pop(field_name)

        if exclude is not None:
            not_allowed = set(exclude)
            for exclude_name in not_allowed:
                self.fields.pop(exclude_name)

This is what you need,use like:

FooSerializer(DynamicFieldsModelSerializer):
    ....

in views.py:

FooSerializer(queryset, many=True, fields=["id", "title"])

or

FooSerializer(queryset, many=True, exclude=['modified', 'active', 'user' ])

Doc is here.

Upvotes: 1

Related Questions