mirror of
https://github.com/encode/django-rest-framework.git
synced 2026-01-11 19:20:54 +03:00
chore: add comments and improve tests
This commit is contained in:
parent
8043317f57
commit
e09a89a1bd
|
|
@ -222,6 +222,28 @@ For example:
|
|||
extra_kwargs = {'client': {'required': False}}
|
||||
validators = [] # Remove a default "unique together" constraint.
|
||||
|
||||
### UniqueConstraint with conditions
|
||||
|
||||
When using Django's `UniqueConstraint` with conditions that reference other model fields, DRF will automatically use
|
||||
`UniqueTogetherValidator` instead of field-level `UniqueValidator`. This ensures proper validation behavior when the constraint
|
||||
effectively involves multiple fields.
|
||||
|
||||
For example, a single-field constraint with a condition becomes a multi-field validation when the condition references other fields.
|
||||
|
||||
class MyModel(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
status = models.CharField(max_length=20)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=['name'],
|
||||
condition=models.Q(status='active'),
|
||||
name='unique_active_name'
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
## Updating nested serializers
|
||||
|
||||
When applying an update to an existing instance, uniqueness validators will
|
||||
|
|
|
|||
|
|
@ -1451,6 +1451,8 @@ class ModelSerializer(Serializer):
|
|||
get_referenced_base_fields_from_q(constraint.condition)
|
||||
)
|
||||
|
||||
# Combine constraint fields and condition fields. If the union
|
||||
# involves multiple fields, treat as unique-together validation
|
||||
required_fields = {*constraint.fields, *condition_fields}
|
||||
if len(required_fields) > 1:
|
||||
yield (
|
||||
|
|
|
|||
|
|
@ -86,6 +86,8 @@ def get_unique_validators(field_name, model_field):
|
|||
if condition is not None
|
||||
else set()
|
||||
)
|
||||
# Only use UniqueValidator if the union of field and condition fields is 1
|
||||
# (i.e. no additional fields referenced in conditions)
|
||||
if len(field_set | condition_fields) == 1:
|
||||
yield UniqueValidator(
|
||||
queryset=queryset if condition is None else queryset.filter(condition),
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ class TestUniquenessTogetherValidation(TestCase):
|
|||
|
||||
def test_is_not_unique_together_condition_based(self):
|
||||
"""
|
||||
Failing unique together validation should result in non field errors when a condition-based
|
||||
Failing unique together validation should result in non-field errors when a condition-based
|
||||
unique together constraint is violated.
|
||||
"""
|
||||
ConditionUniquenessTogetherModel.objects.create(race_name='example', position=1)
|
||||
|
|
@ -275,10 +275,10 @@ class TestUniquenessTogetherValidation(TestCase):
|
|||
'position': 2
|
||||
}
|
||||
|
||||
def test_unique_together_condition_based(self):
|
||||
def test_is_unique_together_condition_based(self):
|
||||
"""
|
||||
In a unique together validation, one field may be non-unique
|
||||
so long as the set as a whole is unique.
|
||||
In a condition-based unique together validation, data is valid when
|
||||
the constrained field differs when the condition applies`.
|
||||
"""
|
||||
ConditionUniquenessTogetherModel.objects.create(race_name='example', position=1)
|
||||
|
||||
|
|
@ -290,6 +290,21 @@ class TestUniquenessTogetherValidation(TestCase):
|
|||
'position': 1
|
||||
}
|
||||
|
||||
def test_is_unique_together_when_condition_does_not_apply(self):
|
||||
"""
|
||||
In a condition-based unique together validation, data is valid when
|
||||
the condition does not apply, even if constrained fields match existing records.
|
||||
"""
|
||||
ConditionUniquenessTogetherModel.objects.create(race_name='example', position=1)
|
||||
|
||||
data = {'race_name': 'example', 'position': 2}
|
||||
serializer = ConditionUniquenessTogetherSerializer(data=data)
|
||||
assert serializer.is_valid()
|
||||
assert serializer.validated_data == {
|
||||
'race_name': 'example',
|
||||
'position': 2
|
||||
}
|
||||
|
||||
def test_updated_instance_excluded_from_unique_together(self):
|
||||
"""
|
||||
When performing an update, the existing instance does not count
|
||||
|
|
@ -308,10 +323,10 @@ class TestUniquenessTogetherValidation(TestCase):
|
|||
When performing an update, the existing instance does not count
|
||||
as a match against uniqueness.
|
||||
"""
|
||||
ConditionUniquenessTogetherModel.objects.create(race_name='example', position=1)
|
||||
instance = ConditionUniquenessTogetherModel.objects.create(race_name='example', position=1)
|
||||
|
||||
data = {'race_name': 'example', 'position': 0}
|
||||
serializer = ConditionUniquenessTogetherSerializer(self.instance, data=data)
|
||||
serializer = ConditionUniquenessTogetherSerializer(instance, data=data)
|
||||
assert serializer.is_valid()
|
||||
assert serializer.validated_data == {
|
||||
'race_name': 'example',
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user