This commit is contained in:
Mark 2012-12-31 04:06:30 -08:00
commit 22745bbefa
4 changed files with 83 additions and 30 deletions

View File

@ -93,7 +93,7 @@ class SerializerOptions(object):
self.exclude = getattr(meta, 'exclude', ()) self.exclude = getattr(meta, 'exclude', ())
class BaseSerializer(Field): class BaseSerializer(WritableField):
class Meta(object): class Meta(object):
pass pass
@ -118,6 +118,7 @@ class BaseSerializer(Field):
self._data = None self._data = None
self._files = None self._files = None
self._errors = None self._errors = None
self._siblings = []
##### #####
# Methods to determine which fields to use when (de)serializing objects. # Methods to determine which fields to use when (de)serializing objects.
@ -276,7 +277,11 @@ class BaseSerializer(Field):
""" """
if hasattr(data, '__iter__') and not isinstance(data, dict): if hasattr(data, '__iter__') and not isinstance(data, dict):
# TODO: error data when deserializing lists # TODO: error data when deserializing lists
return (self.from_native(item) for item in data) for item in data:
sibling = copy.deepcopy(self)
self._siblings.append(sibling)
sibling.object = sibling.from_native(item, None)
return [sibling.object for sibling in self._siblings]
self._errors = {} self._errors = {}
if data is not None or files is not None: if data is not None or files is not None:
@ -361,6 +366,24 @@ class ModelSerializer(Serializer):
""" """
_options_class = ModelSerializerOptions _options_class = ModelSerializerOptions
def field_from_native(self, data, files, field_name, into):
if self.read_only:
return
# TODO handle partial option
# TODO handle errors
# deserialize the nested object
try:
native = data[field_name]
except KeyError:
if self.required:
raise ValidationError(self.error_messages['required'])
return
self.object = self.from_native(native, files)
into[self.source or field_name] = self
def get_default_fields(self): def get_default_fields(self):
""" """
Return all the fields that should be serialized for the model. Return all the fields that should be serialized for the model.
@ -498,11 +521,6 @@ class ModelSerializer(Serializer):
self.m2m_data = {} self.m2m_data = {}
self.related_data = {} self.related_data = {}
if instance is not None:
for key, val in attrs.items():
setattr(instance, key, val)
else:
# Reverse fk relations # Reverse fk relations
for (obj, model) in self.opts.model._meta.get_all_related_objects_with_model(): for (obj, model) in self.opts.model._meta.get_all_related_objects_with_model():
field_name = obj.field.related_query_name() field_name = obj.field.related_query_name()
@ -520,6 +538,10 @@ class ModelSerializer(Serializer):
if field.name in attrs: if field.name in attrs:
self.m2m_data[field.name] = attrs.pop(field.name) self.m2m_data[field.name] = attrs.pop(field.name)
if instance is not None:
for key, val in attrs.items():
setattr(instance, key, val)
else:
instance = self.opts.model(**attrs) instance = self.opts.model(**attrs)
try: try:
@ -530,10 +552,17 @@ class ModelSerializer(Serializer):
return instance return instance
def save(self, save_m2m=True): def save(self, save_m2m=True, parent=None, fk_field=None):
""" """
Save the deserialized object and return it. Save the deserialized object and return it.
""" """
if hasattr(self.object, '__iter__'):
for obj in self._siblings:
obj.save(parent=parent, fk_field=fk_field)
return self.object
if parent and fk_field:
setattr(self.object, fk_field, parent)
self.object.save() self.object.save()
if getattr(self, 'm2m_data', None) and save_m2m: if getattr(self, 'm2m_data', None) and save_m2m:
@ -543,6 +572,10 @@ class ModelSerializer(Serializer):
if getattr(self, 'related_data', None): if getattr(self, 'related_data', None):
for accessor_name, object_list in self.related_data.items(): for accessor_name, object_list in self.related_data.items():
if isinstance(object_list, ModelSerializer):
fk_field = self.object._meta.get_field_by_name(accessor_name)[0].field.name
object_list.save(parent=self.object, fk_field=fk_field)
else:
setattr(self.object, accessor_name, object_list) setattr(self.object, accessor_name, object_list)
self.related_data = {} self.related_data = {}

View File

@ -114,8 +114,8 @@ class HyperlinkedManyToManyTests(TestCase):
instance = ManyToManySource.objects.get(pk=1) instance = ManyToManySource.objects.get(pk=1)
serializer = ManyToManySourceSerializer(instance, data=data) serializer = ManyToManySourceSerializer(instance, data=data)
self.assertTrue(serializer.is_valid()) self.assertTrue(serializer.is_valid())
self.assertEquals(serializer.data, data)
serializer.save() serializer.save()
self.assertEquals(serializer.data, data)
# Ensure source 1 is updated, and everything else is as expected # Ensure source 1 is updated, and everything else is as expected
queryset = ManyToManySource.objects.all() queryset = ManyToManySource.objects.all()
@ -132,8 +132,8 @@ class HyperlinkedManyToManyTests(TestCase):
instance = ManyToManyTarget.objects.get(pk=1) instance = ManyToManyTarget.objects.get(pk=1)
serializer = ManyToManyTargetSerializer(instance, data=data) serializer = ManyToManyTargetSerializer(instance, data=data)
self.assertTrue(serializer.is_valid()) self.assertTrue(serializer.is_valid())
self.assertEquals(serializer.data, data)
serializer.save() serializer.save()
self.assertEquals(serializer.data, data)
# Ensure target 1 is updated, and everything else is as expected # Ensure target 1 is updated, and everything else is as expected
queryset = ManyToManyTarget.objects.all() queryset = ManyToManyTarget.objects.all()
@ -239,8 +239,8 @@ class HyperlinkedForeignKeyTests(TestCase):
instance = ForeignKeyTarget.objects.get(pk=2) instance = ForeignKeyTarget.objects.get(pk=2)
serializer = ForeignKeyTargetSerializer(instance, data=data) serializer = ForeignKeyTargetSerializer(instance, data=data)
self.assertTrue(serializer.is_valid()) self.assertTrue(serializer.is_valid())
self.assertEquals(serializer.data, data)
serializer.save() serializer.save()
self.assertEquals(serializer.data, data)
# Ensure target 2 is update, and everything else is as expected # Ensure target 2 is update, and everything else is as expected
queryset = ForeignKeyTarget.objects.all() queryset = ForeignKeyTarget.objects.all()

View File

@ -80,6 +80,26 @@ class ReverseForeignKeyTests(TestCase):
] ]
self.assertEquals(serializer.data, expected) self.assertEquals(serializer.data, expected)
def test_reverse_foreign_key_create(self):
target = ForeignKeyTarget.objects.get(name='target-2')
# The value for target here should be ignored
data = {'sources': [{'name': u'source-4', 'target': 1},
{'name': u'source-5', 'target': 1}],
'name': u'target-2'
}
expected = {'sources': [{'id': 4, 'name': u'source-4', 'target': 2},
{'id': 5, 'name': u'source-5', 'target': 2}],
'id': 2, 'name': u'target-2'
}
serializer = ForeignKeyTargetSerializer(target, data=data, partial=True)
self.assertTrue(serializer.is_valid())
serializer.save()
# Ensure target 2 has new source and everything else is as expected
target = ForeignKeyTarget.objects.get(name='target-2')
serializer = ForeignKeyTargetSerializer(target)
self.assertEquals(serializer.data, expected)
class NestedNullableForeignKeyTests(TestCase): class NestedNullableForeignKeyTests(TestCase):
def setUp(self): def setUp(self):

View File

@ -99,8 +99,8 @@ class PKManyToManyTests(TestCase):
instance = ManyToManySource.objects.get(pk=1) instance = ManyToManySource.objects.get(pk=1)
serializer = ManyToManySourceSerializer(instance, data=data) serializer = ManyToManySourceSerializer(instance, data=data)
self.assertTrue(serializer.is_valid()) self.assertTrue(serializer.is_valid())
self.assertEquals(serializer.data, data)
serializer.save() serializer.save()
self.assertEquals(serializer.data, data)
# Ensure source 1 is updated, and everything else is as expected # Ensure source 1 is updated, and everything else is as expected
queryset = ManyToManySource.objects.all() queryset = ManyToManySource.objects.all()
@ -117,8 +117,8 @@ class PKManyToManyTests(TestCase):
instance = ManyToManyTarget.objects.get(pk=1) instance = ManyToManyTarget.objects.get(pk=1)
serializer = ManyToManyTargetSerializer(instance, data=data) serializer = ManyToManyTargetSerializer(instance, data=data)
self.assertTrue(serializer.is_valid()) self.assertTrue(serializer.is_valid())
self.assertEquals(serializer.data, data)
serializer.save() serializer.save()
self.assertEquals(serializer.data, data)
# Ensure target 1 is updated, and everything else is as expected # Ensure target 1 is updated, and everything else is as expected
queryset = ManyToManyTarget.objects.all() queryset = ManyToManyTarget.objects.all()
@ -221,8 +221,8 @@ class PKForeignKeyTests(TestCase):
instance = ForeignKeyTarget.objects.get(pk=2) instance = ForeignKeyTarget.objects.get(pk=2)
serializer = ForeignKeyTargetSerializer(instance, data=data) serializer = ForeignKeyTargetSerializer(instance, data=data)
self.assertTrue(serializer.is_valid()) self.assertTrue(serializer.is_valid())
self.assertEquals(serializer.data, data)
serializer.save() serializer.save()
self.assertEquals(serializer.data, data)
# Ensure target 2 is update, and everything else is as expected # Ensure target 2 is update, and everything else is as expected
queryset = ForeignKeyTarget.objects.all() queryset = ForeignKeyTarget.objects.all()