django-rest-framework icon indicating copy to clipboard operation
django-rest-framework copied to clipboard

Serializer `UniqueConstraint` validation fails incorrectly on create with conditional fields

Open nefrob opened this issue 7 months ago • 7 comments

Issue:

Adding the following test to TestUniqueConstraintValidation demonstrates an issue in the serializer’s unique constraint validation logic:

def test_unique_constraint_create(self):
    class SourceUniqueConstraintSerializer(serializers.ModelSerializer):
        raceName = serializers.CharField(source="race_name")

        class Meta:
            model = UniqueConstraintModel
            fields = ("raceName", "position", "global_id", "fancy_conditions")

    UniqueConstraintModel.objects.exclude(pk=self.instance.pk).delete()
    data = {
        "race_name": "other",
        "position": 1,
        "global_id": 3,
        "fancy_conditions": 1,
    }

    obj = UniqueConstraintModel.objects.create(**data)
    obj.delete()

    serializer = SourceUniqueConstraintSerializer(data=data)
    assert serializer.is_valid() # This unexpectedly fails
  • Creating a UniqueConstraintModel instance directly with UniqueConstraintModel.objects.create() succeeds, indicating no unique constraint violation occurs at the database level.
  • However, validating the same data through the ModelSerializer fails.

Root cause: The unique validator used by the serializer does not account for the global_id field during validation of fancy_conditions, even though global_id is part of the conditional logic in the unique constraint.

As a result, the validation incorrectly identifies a conflict based only on fancy_conditions, without considering that the global_id in the input data should exempt it from the constraint.

Expected behavior:

Serializer validation should succeed in this case, consistent with the model-level behavior.

nefrob avatar May 23 '25 19:05 nefrob

can you check this PR please https://github.com/encode/django-rest-framework/pull/9712 ?

auvipy avatar Jun 02 '25 05:06 auvipy

@auvipy this unfortunately doesn't resolve the issue. I think the failure happens in the unique validator call since does not consider the fields included in constraint conditions.

https://github.com/kalekseev/django-rest-framework/blob/f30c0e2eedda410a7e6a0d1b351377a9084361b4/rest_framework/validators.py#L66

nefrob avatar Jun 06 '25 18:06 nefrob

Do you have the capacity to fix this?

auvipy avatar Jun 11 '25 10:06 auvipy

@auvipy I might have some time in the next few weeks to take a look.

nefrob avatar Jun 11 '25 14:06 nefrob

@auvipy tentative fix now up here: https://github.com/encode/django-rest-framework/pull/9744.

nefrob avatar Jul 20 '25 23:07 nefrob

@nefrob @auvipy Do you intend to continue working on this?

Adiorz avatar Oct 16 '25 07:10 Adiorz

@Adiorz just looking for desired direction. If we want something more comprehensive / specific than the above PR, I probably won't have time and would defer to someone else. I can make a few small adjustments on the existing approach if needed however.

nefrob avatar Oct 16 '25 15:10 nefrob