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!
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.
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.
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.
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.
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 withdefault
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With