Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 247
Alex Avatar asked Apr 06 '19 14:04

Alex


2 Answers

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
like image 85
Johan Avatar answered Sep 18 '22 21:09

Johan


DRF tutorial recommend to override perform_create method in this case and then edit serializer so, that it reflect to new field

from rest_framework import generics, serializers
from .models import Post


class PostSerializer(serializers.HyperlinkedModelSerializer):
    author = serializers.ReadOnlyField(source='author.username')

    class Meta:
        model = models.Post
        fields = ['title', 'content', 'author']


class ListPost(generics.ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

    def perform_create(self, serializer):
        return serializer.save(author=self.request.user)
like image 33
Ilya Davydov Avatar answered Sep 18 '22 21:09

Ilya Davydov