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

    Type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions