DRF 3.0: UniqueTogetherValidator with a read-only

2019-04-27 05:09发布

问题:

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!

回答1:

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.