Search code examples
djangodjango-rest-frameworkdjango-serializerdjango-rest-viewsets

Django DRF add request.user to modelserializer


I am using django rest framework, and I have an object being created via a modelviewset, and a modelserializer. This view is only accessible by authenticated users, and the object should set its 'uploaded_by' field, to be that user.

I've read the docs, and come to the conclusion that this should work

viewset:

class FooViewset(viewsets.ModelViewSet):
    permission_classes = [permissions.IsAdminUser]
    queryset = Foo.objects.all()
    serializer_class = FooSerializer

    def get_serializer_context(self):
        return {"request": self.request}

serializer:

class FooSerializer(serializers.ModelSerializer):
    uploaded_by = serializers.PrimaryKeyRelatedField(
        read_only=True, default=serializers.CurrentUserDefault()
    )

    class Meta:
        model = Foo
        fields = "__all__"

However, this results in the following error:

django.db.utils.IntegrityError: NOT NULL constraint failed: bar_foo.uploaded_by_id

Which suggests that "uploaded_by" is not being filled by the serializer.

Based on my understanding of the docs, this should have added the field to the validated data from the serializer, as part of the create method.

Clearly I've misunderstood something!


Solution

  • The problem lies in the read_only attribute on your uploaded_by field:

    Read-only fields are included in the API output, but should not be included in the input during create or update operations. Any 'read_only' fields that are incorrectly included in the serializer input will be ignored.

    Set this to True to ensure that the field is used when serializing a representation, but is not used when creating or updating an instance during deserialization.

    Source

    Basically it's used for showing representation of an object, but is excluded in any update and create-process.

    Instead, you can override the create function to store the desired user by manually assigning it.

    class FooSerializer(serializers.ModelSerializer):
    
        uploaded_by = serializers.PrimaryKeyRelatedField(read_only=True)
    
        def create(self, validated_data):
            foo = Foo.objects.create(
                uploaded_by=self.context['request'].user,
                **validated_data
            )
            return foo