Skip to content

Instantly share code, notes, and snippets.

@alukach
Created April 8, 2016 16:30
Show Gist options
  • Save alukach/272584e54e85d43362dce566fe514313 to your computer and use it in GitHub Desktop.
Save alukach/272584e54e85d43362dce566fe514313 to your computer and use it in GitHub Desktop.
A ModelSerializer that allows for optional fields to be added to a serializer. These fields only appear when they are referred to by name with a `include` GET parameter. Additionally, a user can provide a `fields` GET parameter to limit which fields are serialized for output.
from rest_framework import serializers
class DynamicFieldsModelSerializer(serializers.ModelSerializer):
"""
A ModelSerializer that allows for optional fields to be added to a
serializer. These fields only appear when they are referred to by
name with a `include` GET parameter.
Additionally, a user can provide a `fields` GET parameter to limit
which fields are serialized for output.
"""
def __init__(self, *args, **kwargs):
fields = getattr(self.Meta, 'fields', None) or self.get_fields().keys()
optional_fields = getattr(self.Meta, 'optional_fields', False)
# Combine fields and optional fields into fields attribute
if optional_fields:
if not isinstance(self.Meta.optional_fields, (list, tuple)):
raise Exception("'optional_fields' must be list or tuple")
self.Meta.fields = type(optional_fields)(fields) + self.Meta.optional_fields
# Instantiate the superclass normally
super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)
# Don't apply field-limiting logic when deserializing data
request = self.context.get('request')
if not request:
return
fields = request.GET.getlist('fields')
included = request.GET.getlist('include')
# Drop any fields that are not specified in the `fields` argument.
if fields:
# Accept either comma-separated values or multiple GET params with same name
fields = [f for f in fields for f in f.split(',')]
allowed = set(fields)
existing = set(self.fields.keys())
for field_name in existing - allowed:
self.fields.pop(field_name)
# Omit any optional fields that aren't specified in GET param
elif optional_fields:
# Accept either comma-separated values or multiple GET params with same name
included = [f for f in included for f in f.split(',')]
for optional_field in optional_fields:
if optional_field not in included:
self.fields.pop(optional_field)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment