Skip to content

Instantly share code, notes, and snippets.

@fidiego
Last active March 16, 2016 14:39
Show Gist options
  • Save fidiego/cd007fdb5f335dfc48e8 to your computer and use it in GitHub Desktop.
Save fidiego/cd007fdb5f335dfc48e8 to your computer and use it in GitHub Desktop.
from collections import OrderedDict
from rest_framework import serializers
class SkipField(Exception):
pass
class ExtensibleModelSerializer(serializers.ModelSerializer):
'''
Allows for specifying ``non_native_fields`` in the Meta, which allow for
custom handling of some fields, while letting the ``ModelSerializer`` do
its magic with the rest of the fields.
'''
def save(self, **kwargs):
return super(ExtensibleModelSerializer, self).save(**kwargs)
def to_representation(self, instance):
'''create a representation of the model but skip ``non_native_fields``
1. set aside fields in ``non_native_fields``
2. call `ModelSerializer`'s `to_representation` with `super`
3. get internal representation of ``non_native_fields``
4. update data w/ non_native_fields representation and return data
'''
# 1. set aside non native fields
model_serializer_declared_fields = self._declared_fields
self._declared_fields = OrderedDict({
_f:self._declared_fields[_f] for _f in self._declared_fields if _f not in self.Meta.non_native_fields
})
# 2. call super
ret_data = super(ExtensibleModelSerializer, self).to_representation(instance)
# 3. get internal representation of non_native_fields
self._declared_fields = model_serializer_declared_fields
non_native_fields = OrderedDict({
_f: self._declared_fields for _f in self._declared_fields if _f in self.Meta.non_native_fields
})
non_native_field_data = self.to_representation_non_native_fields(instance, non_native_fields)
# 4. update data w/ non_native_fields and return it
ret_data.update(non_native_field_data)
return ret_data
def to_internal_value(self, data):
model_serializer_writeable_fields = self._declared_fields
self._declared_fields = OrderedDict({
_f: self._declared_fields[_f] for _f in self._declared_fields if _f not in self.Meta.non_native_fields
})
ret_data = super(ExtensibleModelSerializer, self).to_internal_value(data)
self._declared_fields = model_serializer_writeable_fields
non_native_fields = OrderedDict({
_f: self._declared_fields[_f] for _f in self._declared_fields if _f in self.Meta.non_native_fields
})
non_native_field_data = self.to_internal_value_non_native_fields(data, non_native_fields)
ret_data.update(non_native_field_data)
return ret_data
def to_internal_value_non_native_fields(self, data, non_native_fields):
ret_data = {}
for field in non_native_fields:
non_native_fields[field].field_name = field
try:
ret_data[field] = self.get_field_internal_value(non_native_fields[field], data)
except SkipField:
continue
return ret_data
def to_representation_non_native_fields(self, instance, non_native_fields):
ret_data = {}
for field in non_native_fields:
try:
ret_data[field] = self.get_field_representation(field, instance)
except SkipField:
continue
return ret_data
def get_field_representation(self, field, instance):
default_method_name = 'get_{field_name}'.format(field_name=field)
method = getattr(self, default_method_name, None)
if not method:
raise SkipField
return method(instance)
def get_field_internal_value(self, field, data):
default_method_name = 'to_internal_value'
method = getattr(field, default_method_name, None)
if not method:
raise SkipField
return method(data[field.field_name])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment