DRF 根据请求用户添加计算字段

DRF add computed field based on requesting user

我有一个模型,它具有特定于用户模型的多对多字段。现在,为了防止信息泄露,我不想 return 整个相关领域通过其余框架。但是,我想创建某种计算字段,如果请求用户在相关字段中,则 return 为真,否则为假。有没有办法让它工作?

例如,就目前而言,其余框架将列出“user_like”的用户,并且 “user_bookmark”,这是我不想发生的,因此我想将它们排除在序列化之外。但是我想要一个字段,比如说,名为 is_liked,如果 request.user 在 user_like 中,则该字段为真,否则为假。

我当前的设置:

型号

class Post(models.Model):
    title = models.CharField(max_length=100)
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    image = models.ImageField(upload_to='dream_photos')

    description = models.TextField(max_length=500)
    date_added = models.DateTimeField(auto_now_add=True)

    user_like = models.ManyToManyField(User, related_name='likes', blank=True)
    user_bookmark = models.ManyToManyField(
        User, related_name='bookmarks', blank=True)
    total_likes = models.PositiveIntegerField(db_index=True, default=0)

    tags = TaggableManager()

序列化器

class PostSerializer(TaggitSerializer, serializers.ModelSerializer):
    tags = TagListSerializerField()

    class Meta:
        model = Dream
        fields = ('title','user', 'image','description','date_added', 'tags', 'total_likes' )

观看次数


class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.prefetch_related('user').all()
    serializer_class = PostSerializer
    permission_classes = [permissions.IsAuthenticated]

    @action(detail=False, methods=['get'], url_path='current-profile', url_name='current-profile')
    def current_user_posts(self, request):
        # I expected this to add the extra field I required
        # But it does not seem to work as expected
        queryset = self.get_queryset().filter(user=request.user).annotate(
            bookmark=(request.user in "user_bookmark"))

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

请求时的预期行为:

{
        "id": 1,
        "tags": [
            "test"
        ],
        "title": "Tets",
        "image": "http://127.0.0.1:8000/media/post_photos/photo1648638314.jpeg",
    
        "description": "TEst",
        "date_added": "2022-05-20T17:47:55.739431Z",
        "total_likes": 0,
        "user": 1,
        "like": true, // true if current user is in user_like, false otherwise
        "bookmark": true // true if current user is in user_bookmark, false otherwise
    }

实际行为

TypeError: 'in ' 需要字符串作为左操作数,而不是 SimpleLazyObject

编辑 1: 的回答似乎有助于解决错误。不幸的是,带注释的字段似乎没有被序列化器return编辑

编辑后的视图:


class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.prefetch_related('user').all()
    serializer_class = PostSerializer
    permission_classes = [permissions.IsAuthenticated]

    @action(detail=False, methods=['get'], url_path='current-profile', url_name='current-profile')
    def current_user_posts(self, request):
        queryset = self.get_queryset().filter(user=request.user).annotate(
            bookmark=Exists(Post.user_bookmark.through.objects.filter(
                post_id=OuterRef('pk'), user_id=request.user.id))
        )

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

您可以做的是使用 SerializerMethodField 将您的自定义字段添加到序列化程序,并在您的视图中通过 get_serializer_context 传递 request.user。例如:

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.prefetch_related('user').all()
    serializer_class = PostSerializer
    permission_classes = [permissions.IsAuthenticated]

    @action(detail=False, methods=['get'], url_path='current-profile', url_name='current-profile')
    def current_user_posts(self, request):
        queryset = self.get_queryset().filter(user=request.user)
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

    def get_serializer_context(self):
        context = super(PostViewSet, self).get_serializer_context()
        context.update({"request": self.request})
        return context

这允许您通过序列化程序可以使用的 context 发送 request。现在在您的序列化程序中,您可以添加这两个新字段:

class PostSerializer(TaggitSerializer, serializers.ModelSerializer):
    tags = TagListSerializerField()
    bookmark =  serializers.SerializerMethodField()
    like = serializers.SerializerMethodField()
   

    class Meta:
        model = Post
        fields = ('title','user', 'image','description','date_added', 'tags', 'total_likes', 'bookmark', 'like')

    def get_like(self, obj):
        return self.context['request'].user in obj.user_like.all()
   

    def get_bookmark(self, obj):
        return self.context['request'].user in obj.user_bookmark.all()