Skip to content

UniqueTogetherValidator does not work with source kwarg #9442

Closed
@r-thomson

Description

@r-thomson

My team has run into a bug in UniqueTogetherValidator where it does not work properly with fields with the source attribute. If a field has a source that is different than the field's name on the serializer, then that field is effectively ignored when checking for uniqueness violations, and the ValidationError is not raised.

I am using a serializer set up like this. Note that UniqueTogetherValidator is expecting the serializer's name for the fields argument— you'll get an error on this line if you try to provide the source name instead.

class ExampleSerializer(serializers.Serializer):
    list_id = serializers.PrimaryKeyRelatedField(source='list')
    position = serializers.IntegerField(source='ordering_key')

    class Meta:
        validators = [
            UniqueTogetherValidator(
                queryset=ToDoItem.objects.all(),
                fields=['list_id', 'position']
            )
        ]

I believe the bug occurs at this position in the code:

else:
# Ignore validation if all field values are unchanged
checked_values = [
value
for field, value in attrs.items()
if field in self.fields and value != getattr(serializer.instance, field)
]
if checked_values and None not in checked_values and qs_exists(queryset):
field_names = ', '.join(self.fields)
message = self.message.format(field_names=field_names)
raise ValidationError(message, code='unique')

On line 172, we check if field (which comes from source) is in self.fields (which is the name on the serializer). If we're looking at the serializer above, then attrs.keys() would be ['list', 'ordering_key'] and self.fields is ['list_id', 'position'].

This means that checked_values will always be empty, and a ValidationError will never be raised.

Checklist

  • Raised initially as discussion #...
  • This is not a feature request suitable for implementation outside this project. Please elaborate what it is:
    • compatibility fix for new Django/Python version ...
    • other type of bug fix
    • other type of improvement that does not touch existing code or change existing behavior (e.g. wrapper for new Django field)
  • I have reduced the issue to the simplest possible case.

REST Framework version: 3.15.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions