Created
September 8, 2015 14:28
-
-
Save koliber/ae5c2df1468c2fdcac9a to your computer and use it in GitHub Desktop.
`ExtensibleModelSerializer` prototype for rest_framework
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. | |
For any fields which are not read from or set directly on the model using ``ModelSerializer``'s | |
logic, you need to provide a mechanism for: | |
* getting the value from the model | |
* changing the incoming data to an internal representation (for updates and creates) | |
* saving the internal representation in the appropriate place | |
There are three ways to get the value from the model for a field. One way is to add a | |
method named `get_FIELD_NAME`, where `FIELD_NAME` is the name of the non-native field. | |
This method will be passed an instance of the object being serialized and should return a | |
representation value, which is ready for serialization. | |
If you do not wish to implement such a magic method, you can override | |
the ``get_field_representation`` method instead. If you need to fetch some values together | |
instead of one-by-one, you can override the ``to_representation_non_native_fields`` | |
method. | |
Writing values requires two steps. The first step is to convert the incoming data into | |
an internal representation. After that, all of the internal data is verified using the | |
serializer's verification mechanism. Lastly, it needs to be saved somewhere. | |
To change the incoming data into an internal representation, there exist three ways to do it. | |
They are analogous to the ways of getting the value. One way, is to implement a magic method | |
named `to_internal_value_FIELD_NAME`, where `FIELD_NAME` is the name of the non-native field. | |
If you do not wish to implement such a magic method, you can override | |
the ``get_field_internal_value`` method instead. If you need to convert values together | |
instead of one-by-one, you can override the ``to_internal_value_non_native_fields`` | |
method. | |
The last step is to save the data. To do this, override the ``update`` and ``create`` | |
methods in your serializer to handle the additional validated data which was generated | |
by the `to_internal_value` calls on the non_native_fields. | |
""" | |
def save(self, **kwargs): | |
return super(ExtensibleModelSerializer, self).save(**kwargs) | |
def to_representation(self, instance): | |
model_serializer_readable_fields = self._readable_fields | |
self._readable_fields = [ | |
_f for _f in self._readable_fields if _f.field_name not in self.Meta.non_native_fields | |
] | |
ret_data = super(ExtensibleModelSerializer, self).to_representation(instance) | |
self._readable_fields = model_serializer_readable_fields | |
non_native_fields = [ | |
_f for _f in self._readable_fields if _f.field_name in self.Meta.non_native_fields | |
] | |
non_native_field_data = self.to_representation_non_native_fields(instance, | |
non_native_fields) | |
ret_data.update(non_native_field_data) | |
return ret_data | |
def to_internal_value(self, data): | |
model_serializer_writeable_fields = self._writable_fields | |
self._writable_fields = [ | |
_f for _f in self._writable_fields if _f.field_name not in self.Meta.non_native_fields | |
] | |
ret_data = super(ExtensibleModelSerializer, self).to_internal_value(data) | |
self._writable_fields = model_serializer_writeable_fields | |
non_native_fields = [ | |
_f for _f in self._writable_fields if _f.field_name 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: | |
try: | |
ret_data[field.field_name] = self.get_field_internal_value(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.field_name] = 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.field_name) | |
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_{field_name}'.format(field_name=field.field_name) | |
method = getattr(self, default_method_name, None) | |
if not method: | |
raise SkipField | |
return method(data) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Not sure what I'm doing wrong, but i'm getting an error:
'AccountSerializer' object has no attribute '_readable_fields'