Created
May 9, 2015 10:17
-
-
Save jmaicher/5a5230e2a7699d4fd30b to your computer and use it in GitHub Desktop.
(De-)serialize Plain Old Python Objects (POPOs) with Django 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
# encoding: utf-8 | |
from __future__ import absolute_import, unicode_literals | |
import logging | |
from rest_framework import serializers | |
from rest_framework.exceptions import ValidationError | |
log = logging.getLogger(__name__) | |
class POPOListSerializer(serializers.ListSerializer): | |
""" List serializer for Plain Old Python Objects (POPO) """ | |
def build(self): | |
if not hasattr(self, "_errors"): | |
self.is_valid(raise_exception=True) | |
return self.save() | |
class POPOSerializer(serializers.Serializer): | |
""" Serializer for Plain Old Python Objects (POPO) """ | |
ORIGINAL_DATA_ATTR = "__popo_serializer__original_data" | |
class Meta: | |
list_serializer_class = POPOListSerializer | |
def create(self, validated_data): | |
""" | |
:type validated_data: dict | collections.OrderedDict | |
""" | |
assert hasattr(self, "Meta") and getattr(self.Meta, "model", None), "No `Meta.model` defined" | |
ModelClass = self.Meta.model | |
for original_name, field in self.fields.iteritems(): | |
name = field.source | |
if name in validated_data: | |
if isinstance(field, POPOSerializer) and name in validated_data: | |
# Note: We call create directly since the data has already been validated | |
validated_data[name] = field.create(validated_data[name]) | |
elif isinstance(field, serializers.ListSerializer) and isinstance(field.child, POPOSerializer): | |
validated_data[name] = [field.child.create(validated_item) for validated_item in validated_data[name]] | |
try: | |
instance = ModelClass(**validated_data) | |
except TypeError as e: | |
msg = 'Got TypeError (%s) when trying to create %s with data: %s' % (e, ModelClass, validated_data) | |
log.warn(msg) | |
raise ValidationError(msg) | |
# Preserve original data for debugging and later processing | |
setattr(instance, self.ORIGINAL_DATA_ATTR, self.root.initial_data) | |
return instance | |
def build(self): | |
if not hasattr(self, "_errors"): | |
self.is_valid(raise_exception=True) | |
return self.save() | |
def update(self, instance, validated_data): | |
raise NotImplemented("`update()` not implemented.") | |
@classmethod | |
def extract_original_data(cls, instance): | |
data = getattr(instance, cls.ORIGINAL_DATA_ATTR, None) | |
if data is None: | |
log.warn("Could not extract original data from {}".format(instance)) | |
return None | |
return data |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks! Usefull snippet <3