mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-29 13:04:03 +03:00
Fix UniqueTogetherValidator with field sources (#7086)
* Add failing tests for unique_together+source * Fix UniqueTogetherValidator source handling * Fix read-only+default+source handling * Update test to use functional serializer * Test UniqueTogetherValidator error+source
This commit is contained in:
parent
f744da74d2
commit
236667b717
|
@ -448,7 +448,7 @@ class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
|
||||||
default = field.get_default()
|
default = field.get_default()
|
||||||
except SkipField:
|
except SkipField:
|
||||||
continue
|
continue
|
||||||
defaults[field.field_name] = default
|
defaults[field.source] = default
|
||||||
|
|
||||||
return defaults
|
return defaults
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,7 @@ class UniqueTogetherValidator:
|
||||||
missing_items = {
|
missing_items = {
|
||||||
field_name: self.missing_message
|
field_name: self.missing_message
|
||||||
for field_name in self.fields
|
for field_name in self.fields
|
||||||
if field_name not in attrs
|
if serializer.fields[field_name].source not in attrs
|
||||||
}
|
}
|
||||||
if missing_items:
|
if missing_items:
|
||||||
raise ValidationError(missing_items, code='required')
|
raise ValidationError(missing_items, code='required')
|
||||||
|
@ -115,17 +115,23 @@ class UniqueTogetherValidator:
|
||||||
"""
|
"""
|
||||||
Filter the queryset to all instances matching the given attributes.
|
Filter the queryset to all instances matching the given attributes.
|
||||||
"""
|
"""
|
||||||
|
# field names => field sources
|
||||||
|
sources = [
|
||||||
|
serializer.fields[field_name].source
|
||||||
|
for field_name in self.fields
|
||||||
|
]
|
||||||
|
|
||||||
# If this is an update, then any unprovided field should
|
# If this is an update, then any unprovided field should
|
||||||
# have it's value set based on the existing instance attribute.
|
# have it's value set based on the existing instance attribute.
|
||||||
if serializer.instance is not None:
|
if serializer.instance is not None:
|
||||||
for field_name in self.fields:
|
for source in sources:
|
||||||
if field_name not in attrs:
|
if source not in attrs:
|
||||||
attrs[field_name] = getattr(serializer.instance, field_name)
|
attrs[source] = getattr(serializer.instance, source)
|
||||||
|
|
||||||
# Determine the filter keyword arguments and filter the queryset.
|
# Determine the filter keyword arguments and filter the queryset.
|
||||||
filter_kwargs = {
|
filter_kwargs = {
|
||||||
field_name: attrs[field_name]
|
source: attrs[source]
|
||||||
for field_name in self.fields
|
for source in sources
|
||||||
}
|
}
|
||||||
return qs_filter(queryset, **filter_kwargs)
|
return qs_filter(queryset, **filter_kwargs)
|
||||||
|
|
||||||
|
|
|
@ -301,6 +301,49 @@ class TestUniquenessTogetherValidation(TestCase):
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def test_read_only_fields_with_default_and_source(self):
|
||||||
|
class ReadOnlySerializer(serializers.ModelSerializer):
|
||||||
|
name = serializers.CharField(source='race_name', default='test', read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = UniquenessTogetherModel
|
||||||
|
fields = ['name', 'position']
|
||||||
|
validators = [
|
||||||
|
UniqueTogetherValidator(
|
||||||
|
queryset=UniquenessTogetherModel.objects.all(),
|
||||||
|
fields=['name', 'position']
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
serializer = ReadOnlySerializer(data={'position': 1})
|
||||||
|
assert serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
|
def test_writeable_fields_with_source(self):
|
||||||
|
class WriteableSerializer(serializers.ModelSerializer):
|
||||||
|
name = serializers.CharField(source='race_name')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = UniquenessTogetherModel
|
||||||
|
fields = ['name', 'position']
|
||||||
|
validators = [
|
||||||
|
UniqueTogetherValidator(
|
||||||
|
queryset=UniquenessTogetherModel.objects.all(),
|
||||||
|
fields=['name', 'position']
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
serializer = WriteableSerializer(data={'name': 'test', 'position': 1})
|
||||||
|
assert serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
|
# Validation error should use seriazlier field name, not source
|
||||||
|
serializer = WriteableSerializer(data={'position': 1})
|
||||||
|
assert not serializer.is_valid()
|
||||||
|
assert serializer.errors == {
|
||||||
|
'name': [
|
||||||
|
'This field is required.'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
def test_allow_explict_override(self):
|
def test_allow_explict_override(self):
|
||||||
"""
|
"""
|
||||||
Ensure validators can be explicitly removed..
|
Ensure validators can be explicitly removed..
|
||||||
|
@ -357,13 +400,9 @@ class TestUniquenessTogetherValidation(TestCase):
|
||||||
def filter(self, **kwargs):
|
def filter(self, **kwargs):
|
||||||
self.called_with = kwargs
|
self.called_with = kwargs
|
||||||
|
|
||||||
class MockSerializer:
|
|
||||||
def __init__(self, instance):
|
|
||||||
self.instance = instance
|
|
||||||
|
|
||||||
data = {'race_name': 'bar'}
|
data = {'race_name': 'bar'}
|
||||||
queryset = MockQueryset()
|
queryset = MockQueryset()
|
||||||
serializer = MockSerializer(instance=self.instance)
|
serializer = UniquenessTogetherSerializer(instance=self.instance)
|
||||||
validator = UniqueTogetherValidator(queryset, fields=('race_name',
|
validator = UniqueTogetherValidator(queryset, fields=('race_name',
|
||||||
'position'))
|
'position'))
|
||||||
validator.filter_queryset(attrs=data, queryset=queryset, serializer=serializer)
|
validator.filter_queryset(attrs=data, queryset=queryset, serializer=serializer)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user