Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DRF 3.0: UniqueTogetherValidator with a read-only field

In process of upgrading to Django REST Framework 3.0 from 2.4.4 and I want to have a read-only user field, but this is failing because 'user' is being required by the UniqueTogetherValidator (I think)

I have model (excuse typos, this is simplified and the code works fine IRL):

class ExampleModel(models.Model):
    some_attr = models.PositiveSmallIntegerField()
    other_attr = models.PositiveSmallIntegerField()
    user = models.ForeignKey(User)

    class Meta:
        unique_together = ('some_attr', 'other_attr', 'user')

Viewset:

class ExampleViewSet(viewsets.ModelViewSet):
    queryset = ExampleModel.objects.all()
    serializer_class = ExampleSerializer

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

    def perform_update(self, serializer):
        serializer.save(user=self.request.user)

Serializer:

class ExampleSerializer(serializers.ModelSerializer):
    user = UserSerializer(read_only=True)

    class Meta:
        model = ExampleModel

Now, I keep getting errors saying: {"user":["This field is required."]}, which was not the case before. In a slightly different example with the same basic problem, I get the assertion error May not set both 'read_only' and 'required' even though I am not setting user as required.

I receive the same error regardless if I add required=False for the user attribute in the serializer, or if I add user to the the excluded fields in the serializer's meta.

When I use the handy new serializer printing, I see:

class Meta:
    validators = [UniqueTogetherValidator(queryset=ExampleModel.objects.all(), fields=('user', 'some_attr', 'other_attr'))]

which gets automatically added based on the model's unique_together. If I explicitly overwrite this and do not include 'user' in the fields for UniqueTogetherValidator then everything works as before.

Is this an intended consequence of the 3.0 update? Seems to me that adding request.user in the perform_create / perform_update is very standard DRF procedure as demonstrated in the tutorial. I realize not having the new validation just means failing at the DB level instead, and the new validation probably gives better error messages, but

Is there a solution other than to override the validation for every serializer where this is an issue?

Thanks in advance for any help!

like image 637
baylee Avatar asked Dec 01 '14 23:12

baylee


People also ask

What is uniquetogethervalidator class in Salesforce?

Note: The UniqueTogetherValidator class always imposes an implicit constraint that all the fields it applies to are always treated as required. Fields with default values are an exception to this as they always supply a value even when omitted from user input.

What is the issue with uniquetogthervalidator in Django REST framework?

This is a known issue that we are in the process of addressing within Django REST Framework. As of right now, there is a note in the documentation about UniqueTogtherValidator that says Note: The UniqueTogetherValidation class always imposes an implicit constraint that all the fields it applies to are always treated as required.

What are the limitations of uniquetogethervalidation?

Note: The UniqueTogetherValidation class always imposes an implicit constraint that all the fields it applies to are always treated as required. Fields with default values are an exception to this as they always supply a value even when omitted from user input.

What is the use of uniquefor<range>validator class?

Note: The UniqueFor<Range>Validator classes impose an implicit constraint that the fields they are applied to are always treated as required. Fields with default values are an exception to this as they always supply a value even when omitted from user input.


1 Answers

This is a known issue that we are in the process of addressing within Django REST Framework. As of right now, there is a note in the documentation about UniqueTogtherValidator that says

Note: The UniqueTogetherValidation class always imposes an implicit constraint that all the fields it applies to are always treated as required. Fields with default values are an exception to this as they always supply a value even when omitted from user input.

This explains why you are seeing an error because the field is required, even though you are explicitly settings read_only=True. You may want to look into the CurrentUserDefault class, which may suit your needs while avoiding the issue with the UniqueTogetherValidator.

class ExampleSerializer(serializers.ModelSerializer):
    user = UserSerializer(
        read_only=True
        default=serializers.CurrentUserDefault()
    )

    class Meta:
        model = ExampleModel

This should do the same thing as your perform_create and perform_update hooks.

like image 132
Kevin Brown-Silva Avatar answered Oct 21 '22 07:10

Kevin Brown-Silva