How to set current user to user field in Django Rest Framework?

前端 未结 9 1980
后悔当初
后悔当初 2020-12-02 15:30

I have the following code working perfectly. I can create a Post object from DRF panel by selecting an image and a user. However I want DRF to populate the user

相关标签:
9条回答
  • 2020-12-02 15:38

    Try this:

    def post(self, request, format=None)
    
            serializer = ProjectSerializer(data=request.data)
            request.data['user'] = request.user.id
    
    
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data, status=status.HTTP_201_CREATED)
    
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST
    
    0 讨论(0)
  • 2020-12-02 15:38

    This is what works for me in serializers.py, where I am also using nested data. I want to display created_by_username without having to lookup other users.

    class ListSerializer(serializers.ModelSerializer):
        """
        A list may be created with items
        """
        items = ItemSerializer(many=True)
    
        # automatically set created_by_id as the current user's id
        created_by_id = serializers.PrimaryKeyRelatedField(
            read_only=True,
        )
    
        created_by_username = serializers.PrimaryKeyRelatedField(
            read_only=True
        )
    
    
        class Meta:
            model = List
            fields = ('id', 'name', 'description', 'is_public',
                'slug', 'created_by_id', 'created_by_username', 'created_at',
                'modified_by', 'modified_at', 'items')
    
        def create(self, validated_data):
            items_data = validated_data.pop('items', None)
            validated_data['created_by_id'] = self.context['request'].user
            validated_data['created_by_username'] = self.context['request'].user.username
            newlist = List.objects.create(**validated_data)
    
            for item_data in items_data:
                Item.objects.create(list=newlist, **item_data)
            return newlist
    
    0 讨论(0)
  • 2020-12-02 15:40

    You will have to override the default behavior of how generics.ListCreateAPIView creates an object.

    class PhotoListAPIView(generics.ListCreateAPIView):
        queryset = Post.objects.filter(hidden=False)
        authentication_classes = (SessionAuthentication, BasicAuthentication)
        permission_classes = (IsAuthenticated,)
    
        def get_serializer_class(self):
            if self.request.method == 'POST':
                return CreatePostSerializer
            else:
                return ListPostSerializer
    
        def create(self, request, *args, **kwargs):
            # Copy parsed content from HTTP request
            data = request.data.copy()
    
            # Add id of currently logged user
            data['user'] = request.user.id
    
            # Default behavior but pass our modified data instead
            serializer = self.get_serializer(data=data)
            serializer.is_valid(raise_exception=True)
            self.perform_create(serializer)
            headers = self.get_success_headers(serializer.data)
            return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
    

    The .get_serializer_class() is not necessary as you can specify which fields are read-only from your serializer, but based on the projects I have worked on, I usually end up with 'asymmetric' serializers, i.e. different serializers depending on the intended operation.

    0 讨论(0)
  • 2020-12-02 15:42

    It depends on your use case. If you want it to be "write-only", meaning DRF automatically populates the field on write and doesn't return the User on read, the most straight-forward implementation according to the docs would be with a HiddenField:

    class PhotoListAPIView(generics.ListCreateAPIView):
        user = serializers.HiddenField(
            default=serializers.CurrentUserDefault(),
        )
    

    If you want want it to be readable, you could use a PrimaryKeyRelatedField while being careful that your serializer pre-populates the field on write - otherwise a user could set the user field pointing to some other random User.

    class PhotoListAPIView(generics.ListCreateAPIView):
        user = serializers.PrimaryKeyRelatedField(
            # set it to read_only as we're handling the writing part ourselves
            read_only=True,
        )
    
        def perform_create(self, serializer):
            serializer.save(user=self.request.user)
    

    Finally, note that if you're using the more verbose APIView instead of generics.ListCreateAPIView, you have to overwrite create instead of perform_create like so:

    class PhotoListAPIView(generics.ListCreateAPIView):
        user = serializers.PrimaryKeyRelatedField(
            read_only=True,
        )
    
        def create(self, validated_data):
            # add the current User to the validated_data dict and call
            # the super method which basically only creates a model
            # instance with that data
            validated_data['user'] = self.request.user
            return super(PhotoListAPIView, self).create(validated_data)
    
    0 讨论(0)
  • 2020-12-02 15:52

    You can use CurrentUserDefault:

    user = serializers.PrimaryKeyRelatedField(
        read_only=True, 
        default=serializers.CurrentUserDefault()
    )
    
    0 讨论(0)
  • 2020-12-02 15:56

    Off the top of my head, you can just override the perform_create() method:

    class PhotoListAPIView(generics.ListCreateAPIView):
        ...
        def perform_create(self, serializer):
            serializer.save(user=self.request.user)
    

    Give that a shot and let me know if it works

    0 讨论(0)
提交回复
热议问题