From e32e464d452b35b49a16fe40d153c56c669102e5 Mon Sep 17 00:00:00 2001 From: Craig de Stigter Date: Fri, 6 Sep 2013 16:18:01 +1200 Subject: [PATCH 1/2] Make modelserializer support writable properties on models it instantiates. --- rest_framework/serializers.py | 18 ++++++++++------- rest_framework/tests/models.py | 26 +++++++++++++++++++++---- rest_framework/tests/test_serializer.py | 23 +++++++++++++++++++++- 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index a63c7f6c2..0b7fa5942 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -858,14 +858,18 @@ class ModelSerializer(Serializer): if isinstance(self.fields.get(field_name, None), Serializer): nested_forward_relations[field_name] = attrs[field_name] - # Update an existing instance... - if instance is not None: - for key, val in attrs.items(): - setattr(instance, key, val) + if instance is None: + # Create a new instance, if we need to + field_attrs = {} + for field_name in meta.get_all_field_names(): + if field_name in attrs: + field_attrs[field_name] = attrs.pop(field_name) - # ...or create a new instance - else: - instance = self.opts.model(**attrs) + instance = self.opts.model(**field_attrs) + + # Update an existing instance... + for key, val in attrs.items(): + setattr(instance, key, val) # Any relations that cannot be set until we've # saved the model get hidden away on these diff --git a/rest_framework/tests/models.py b/rest_framework/tests/models.py index 1598ecd94..0c49dcc92 100644 --- a/rest_framework/tests/models.py +++ b/rest_framework/tests/models.py @@ -81,10 +81,16 @@ class Person(RESTFrameworkModel): @property def info(self): - return { - 'name': self.name, - 'age': self.age, - } + if not hasattr(self, '_info'): + self._info = { + 'name': self.name, + 'age': self.age, + } + return self._info + + @info.setter + def info(self, value): + self._info = value class BlogPost(RESTFrameworkModel): @@ -163,6 +169,18 @@ class NullableOneToOneSource(RESTFrameworkModel): related_name='nullable_source') +class ModelWithWritableProperty(RESTFrameworkModel): + name = models.CharField(max_length=100) + + @property + def prop(self): + return getattr(self, '_value', 'hi!') + + @prop.setter + def prop(self, value): + self._value = value + + # Serializer used to test BasicModel class BasicModelSerializer(serializers.ModelSerializer): class Meta: diff --git a/rest_framework/tests/test_serializer.py b/rest_framework/tests/test_serializer.py index c24976603..d6d1d83b5 100644 --- a/rest_framework/tests/test_serializer.py +++ b/rest_framework/tests/test_serializer.py @@ -7,7 +7,7 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers, fields, relations from rest_framework.tests.models import (HasPositiveIntegerAsChoice, Album, ActionItem, Anchor, BasicModel, BlankFieldModel, BlogPost, BlogPostComment, Book, CallableDefaultValueModel, DefaultValueModel, - ManyToManyModel, Person, ReadOnlyManyToManyModel, Photo, RESTFrameworkModel) + ManyToManyModel, Person, ReadOnlyManyToManyModel, Photo, RESTFrameworkModel, ModelWithWritableProperty) from rest_framework.tests.models import BasicModelSerializer import datetime import pickle @@ -1643,3 +1643,24 @@ class SerializerSupportsManyRelationships(TestCase): serializer = SimpleSlugSourceModelSerializer(data={'text': 'foo', 'targets': [1, 2]}) self.assertTrue(serializer.is_valid()) self.assertEqual(serializer.data, {'text': 'foo', 'targets': [1, 2]}) + + +class WritablePropertyModelSerializer(serializers.ModelSerializer): + prop = serializers.CharField(source='prop') + class Meta: + model = ModelWithWritableProperty + fields = ('name', 'prop',) + + +class ModelSerializerSupportsWritableProperty(TestCase): + def setUp(self): + ModelWithWritableProperty.objects.create(name='hey! ') + + def test_modelserializer_create_with_property(self): + s = WritablePropertyModelSerializer(data={ + 'name': 'the name', + 'prop': 'new value', + }) + self.assertTrue(s.is_valid()) + obj = s.save() + self.assertEqual(obj.prop, 'new value') From a5856beb03b63528a08dfd1fb624c12848320da3 Mon Sep 17 00:00:00 2001 From: Craig de Stigter Date: Fri, 6 Sep 2013 16:35:07 +1200 Subject: [PATCH 2/2] add comment referencing ticket --- rest_framework/tests/test_serializer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rest_framework/tests/test_serializer.py b/rest_framework/tests/test_serializer.py index d6d1d83b5..379ee7e8d 100644 --- a/rest_framework/tests/test_serializer.py +++ b/rest_framework/tests/test_serializer.py @@ -1645,6 +1645,8 @@ class SerializerSupportsManyRelationships(TestCase): self.assertEqual(serializer.data, {'text': 'foo', 'targets': [1, 2]}) +### Regression test for #1088 + class WritablePropertyModelSerializer(serializers.ModelSerializer): prop = serializers.CharField(source='prop') class Meta: