Skip to content

KeyError in UniqueTogetherValidator when condition field missing in attrs during partial update #9756

@jan-schankin

Description

@jan-schankin

I've got a KeyError on line condition_kwargs = {source: attrs[source] for source in condition_sources} in the __call__ method of UniqueTogetherValidator class validating unique constraint with condition during partial update, because the condition field was not in attrs:

    def __call__(self, attrs, serializer):
        self.enforce_required_fields(attrs, serializer)
        queryset = self.queryset
        queryset = self.filter_queryset(attrs, queryset, serializer)
        queryset = self.exclude_current_instance(attrs, queryset, serializer.instance)

        checked_names = [
            serializer.fields[field_name].source for field_name in self.fields
        ]
        # Ignore validation if any field is None
        if serializer.instance is None:
            checked_values = [attrs[field_name] for field_name in checked_names]
        else:
            # Ignore validation if all field values are unchanged
            checked_values = [
                attrs[field_name]
                for field_name in checked_names
                if attrs[field_name] != getattr(serializer.instance, field_name)
            ]

        condition_sources = (serializer.fields[field_name].source for field_name in self.condition_fields)
        condition_kwargs = {source: attrs[source] for source in condition_sources}  #  here is the issue
        if checked_values and None not in checked_values and qs_exists_with_condition(queryset, self.condition, condition_kwargs):
            field_names = ', '.join(self.fields)
            message = self.message.format(field_names=field_names)
            raise ValidationError(message, code='unique')

I assume that if source is not present in attrs, it should fall back to getattr(serializer.instance, source), something like:

condition_kwargs = {}
for source in condition_sources:
    if source in attrs:
        condition_kwargs[source] = attrs[source]
    elif serializer.instance is not None:
        condition_kwargs[source] = getattr(serializer.instance, source)

Similarly as this is done in the 'filter_queryset' method.

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