Reputation: 2452
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
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