mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-08-04 04:20:12 +03:00
Fix issue when serializing certain foreign keys.
If a foreign key has a default value that is not an instance of the related model (i.e. its a value for the primary key for that model), the foreign key field has a uniqueness constraint, and the field was not provided as data for the serializer, then we need to set that default value to the attname of the field when creating the model instance. Otherwise, a ValueError will be raised when creating the model instance, saying 'Cannot assign "<default value>": "<field>" must be a "<related_model>" instance.' This change ensures that the kwargs passed to model instance creation are modified when appropriate to account for this. Signed-off-by: Tommy Beadle <tbeadle@gmail.com>
This commit is contained in:
parent
2a1fd3b45a
commit
34d2f7c529
|
@ -909,8 +909,14 @@ class ModelSerializer(Serializer):
|
|||
info = model_meta.get_field_info(ModelClass)
|
||||
many_to_many = {}
|
||||
for field_name, relation_info in info.relations.items():
|
||||
if relation_info.to_many and (field_name in validated_data):
|
||||
if field_name in validated_data:
|
||||
if relation_info.to_many:
|
||||
many_to_many[field_name] = validated_data.pop(field_name)
|
||||
elif not isinstance(
|
||||
validated_data[field_name],
|
||||
relation_info.related_model):
|
||||
validated_data[relation_info.model_field.attname] = \
|
||||
validated_data.pop(field_name)
|
||||
|
||||
try:
|
||||
instance = ModelClass.objects.create(**validated_data)
|
||||
|
@ -1368,7 +1374,7 @@ class ModelSerializer(Serializer):
|
|||
elif getattr(unique_constraint_field, 'auto_now', None):
|
||||
default = timezone.now
|
||||
elif unique_constraint_field.has_default():
|
||||
default = unique_constraint_field.default
|
||||
default = unique_constraint_field.get_default()
|
||||
else:
|
||||
default = empty
|
||||
|
||||
|
|
|
@ -61,6 +61,19 @@ class ForeignKeySource(RESTFrameworkModel):
|
|||
on_delete=models.CASCADE)
|
||||
|
||||
|
||||
class ForeignKeyInUniquenessConstraintSource(RESTFrameworkModel):
|
||||
name = models.CharField(max_length=100)
|
||||
target = models.ForeignKey(ForeignKeyTarget,
|
||||
related_name='sources_with_uniqueness',
|
||||
help_text='Target',
|
||||
verbose_name='Target',
|
||||
default=1,
|
||||
on_delete=models.CASCADE)
|
||||
|
||||
class Meta:
|
||||
unique_together = ('name', 'target')
|
||||
|
||||
|
||||
# Nullable ForeignKey
|
||||
class NullableForeignKeySource(RESTFrameworkModel):
|
||||
name = models.CharField(max_length=100)
|
||||
|
|
|
@ -5,8 +5,9 @@ from django.utils import six
|
|||
|
||||
from rest_framework import serializers
|
||||
from tests.models import (
|
||||
ForeignKeySource, ForeignKeyTarget, ManyToManySource, ManyToManyTarget,
|
||||
NullableForeignKeySource, NullableOneToOneSource, NullableUUIDForeignKeySource,
|
||||
ForeignKeyInUniquenessConstraintSource, ForeignKeySource, ForeignKeyTarget,
|
||||
ManyToManySource, ManyToManyTarget, NullableForeignKeySource,
|
||||
NullableOneToOneSource, NullableUUIDForeignKeySource,
|
||||
OneToOnePKSource, OneToOneTarget, UUIDForeignKeyTarget
|
||||
)
|
||||
|
||||
|
@ -37,6 +38,12 @@ class ForeignKeySourceSerializer(serializers.ModelSerializer):
|
|||
fields = ('id', 'name', 'target')
|
||||
|
||||
|
||||
class ForeignKeyUniquenessSourceSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = ForeignKeyInUniquenessConstraintSource
|
||||
fields = ('id', 'name', 'target')
|
||||
|
||||
|
||||
# Nullable ForeignKey
|
||||
class NullableForeignKeySourceSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
|
@ -360,6 +367,35 @@ class PKForeignKeyTests(TestCase):
|
|||
assert 'target' not in serializer.validated_data
|
||||
|
||||
|
||||
class PKForeignKeyInUniquenessConstraintTests(TestCase):
|
||||
def test_use_default_value_for_fk(self):
|
||||
default_val = ForeignKeyInUniquenessConstraintSource._meta.get_field(
|
||||
'target'
|
||||
).default
|
||||
target = ForeignKeyTarget.objects.create(pk=default_val)
|
||||
serializer = ForeignKeyUniquenessSourceSerializer(
|
||||
data={'name': 'source-1'}
|
||||
)
|
||||
assert serializer.is_valid()
|
||||
serializer.save()
|
||||
source = ForeignKeyInUniquenessConstraintSource.objects.get(
|
||||
name='source-1',
|
||||
)
|
||||
assert source.target == target
|
||||
|
||||
def test_provide_value_for_fk(self):
|
||||
target = ForeignKeyTarget.objects.create(pk=10)
|
||||
serializer = ForeignKeyUniquenessSourceSerializer(
|
||||
data={'name': 'source-2', 'target': target.pk}
|
||||
)
|
||||
assert serializer.is_valid()
|
||||
serializer.save()
|
||||
source = ForeignKeyInUniquenessConstraintSource.objects.get(
|
||||
name='source-2',
|
||||
)
|
||||
assert source.target == target
|
||||
|
||||
|
||||
class PKNullableForeignKeyTests(TestCase):
|
||||
def setUp(self):
|
||||
target = ForeignKeyTarget(name='target-1')
|
||||
|
|
Loading…
Reference in New Issue
Block a user