ModelSerializer.create() to handle many to many by default

This commit is contained in:
Tom Christie 2014-09-18 14:58:08 +01:00
parent 9fdb2280d1
commit 106362b437
3 changed files with 67 additions and 3 deletions

View File

@ -24,7 +24,10 @@ class RelatedField(Field):
# We override this method in order to automagically create
# `ManyRelation` classes instead when `many=True` is set.
if kwargs.pop('many', False):
return ManyRelation(child_relation=cls(*args, **kwargs))
return ManyRelation(
child_relation=cls(*args, **kwargs),
read_only=kwargs.get('read_only', False)
)
return super(RelatedField, cls).__new__(cls, *args, **kwargs)
def get_queryset(self):

View File

@ -344,7 +344,25 @@ class ModelSerializer(Serializer):
def create(self, attrs):
ModelClass = self.Meta.model
return ModelClass.objects.create(**attrs)
# Remove many-to-many relationships from attrs.
# They are not valid arguments to the default `.create()` method,
# as they require that the instance has already been saved.
info = model_meta.get_field_info(ModelClass)
many_to_many = {}
for key, relation_info in info.relations.items():
if relation_info.to_many and (key in attrs):
many_to_many[key] = attrs.pop(key)
instance = ModelClass.objects.create(**attrs)
# Save many to many relationships after the instance is created.
if many_to_many:
for key, value in many_to_many.items():
setattr(instance, key, value)
instance.save()
return instance
def update(self, obj, attrs):
for attr, value in attrs.items():

View File

@ -360,7 +360,7 @@ class TestIntegration(TestCase):
self.serializer_cls = TestSerializer
def test_pk_relationship_representations(self):
def test_pk_retrival(self):
serializer = self.serializer_cls(self.instance)
expected = {
'id': self.instance.pk,
@ -370,3 +370,46 @@ class TestIntegration(TestCase):
'through': []
}
self.assertEqual(serializer.data, expected)
def test_pk_create(self):
new_foreign_key = ForeignKeyTargetModel.objects.create(
name='foreign_key'
)
new_one_to_one = OneToOneTargetModel.objects.create(
name='one_to_one'
)
new_many_to_many = [
ManyToManyTargetModel.objects.create(
name='new many_to_many (%d)' % idx
) for idx in range(3)
]
data = {
'foreign_key': new_foreign_key.pk,
'one_to_one': new_one_to_one.pk,
'many_to_many': [item.pk for item in new_many_to_many],
}
# Serializer should validate okay.
serializer = self.serializer_cls(data=data)
assert serializer.is_valid()
# Creating the instance, relationship attributes should be set.
instance = serializer.save()
assert instance.foreign_key.pk == new_foreign_key.pk
assert instance.one_to_one.pk == new_one_to_one.pk
assert [
item.pk for item in instance.many_to_many.all()
] == [
item.pk for item in new_many_to_many
]
assert list(instance.through.all()) == []
# Representation should be correct.
expected = {
'id': instance.pk,
'foreign_key': new_foreign_key.pk,
'one_to_one': new_one_to_one.pk,
'many_to_many': [item.pk for item in new_many_to_many],
'through': []
}
self.assertEqual(serializer.data, expected)