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

Bulk patch fails with unique_together models

Open melinath opened this issue 7 years ago • 3 comments

I ran into an issue with a model I was trying to bulk patch that also has a unique_together constraint. Traceback is below. The issue here is that the UniqueTogetherValidator expects to be called from a serializer with a single related instance (i.e. a normal update serializer). But the serializer is in fact instantiated with a queryset.

TBH I don't entirely understand why serializer.instance is being set to a queryset. But anyway.

Traceback (most recent call last):
  File ".../django/core/handlers/base.py", line 149, in get_response
    response = self.process_exception_by_middleware(e, request)
  File ".../django/core/handlers/base.py", line 147, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File ".../django/views/decorators/csrf.py", line 58, in wrapped_view
    return view_func(*args, **kwargs)
  File ".../rest_framework/viewsets.py", line 87, in view
    return self.dispatch(request, *args, **kwargs)
  File ".../rest_framework/views.py", line 466, in dispatch
    response = self.handle_exception(exc)
  File ".../rest_framework/views.py", line 463, in dispatch
    response = handler(request, *args, **kwargs)
  File ".../rest_framework_bulk/drf3/mixins.py", line 79, in partial_bulk_update
    return self.bulk_update(request, *args, **kwargs)
  File ".../rest_framework_bulk/drf3/mixins.py", line 73, in bulk_update
    serializer.is_valid(raise_exception=True)
  File ".../rest_framework/serializers.py", line 213, in is_valid
    self._validated_data = self.run_validation(self.initial_data)
  File ".../rest_framework/serializers.py", line 557, in run_validation
    value = self.to_internal_value(data)
  File ".../rest_framework/serializers.py", line 593, in to_internal_value
    validated = self.child.run_validation(item)
  File ".../rest_framework/serializers.py", line 409, in run_validation
    self.run_validators(value)
  File ".../rest_framework/fields.py", line 498, in run_validators
    validator(value)
  File ".../rest_framework/validators.py", line 142, in __call__
    queryset = self.exclude_current_instance(attrs, queryset)
  File ".../rest_framework/validators.py", line 135, in exclude_current_instance
    return queryset.exclude(pk=self.instance.pk)
AttributeError: 'QuerySet' object has no attribute 'pk'

My workaround was to take advantage of the 'id' attribute that the bulk serializer keeps track of and write a custom unique_together validator.

# validators.py
from rest_framework.validators import UniqueTogetherValidator


class BulkUniqueTogetherValidator(UniqueTogetherValidator):
    def exclude_current_instance(self, attrs, queryset):
        if attrs.get('id'):
            return queryset.exclude(pk=attrs['id'])
        return queryset
# serializers.py
from .validators import BulkUniqueTogetherValidator


class ThingSerializer(serializers.HyperlinkedModelSerializer):
    <...properties>

    def get_unique_together_validators(self):
        return [BulkUniqueTogetherValidator(
            queryset=Thing.objects.all(),
            fields=('field1', 'field2'),
        )]

melinath avatar Oct 25 '16 04:10 melinath

Hi @melinath , I see the same issue when trying to implement as in README instructions, could you please elaborate on the workaround? I have a simple model with 2 fields, BulkSerializer and nothing custom. Thanks!

msatish9 avatar Dec 05 '16 18:12 msatish9

Meet this bug same

ibuler avatar Dec 27 '16 11:12 ibuler

same here.

crazy-canux avatar Jul 05 '21 10:07 crazy-canux