Django (django-rest-framework) 寻找最佳实践来找出 request.user 是否存在于 blog.likes

Django (django-rest-framework) looking for a best practice to find out wether request.user exists in blog.likes

为简单起见,假设我在 Django (django-rest-framework) 应用程序中有以下模型。

User (id, name)

BlogPost (id, user, content, image)

BlogPostLikes(id, user, blogpost, timestamp) 
// Assuming an inverse-relation to BlogPost via a variable called "likes"

我正在寻找一种 "best-practice" 的方式来:

高效!

我的直觉让我想像这样实现它(使用基于 class 的通用视图)

// views.py
class BlogPostList(ListAPIView):
    queryset = BlogPost.objects.all()
    serializer_class = BlogPostSerializer

    def get(self, request, *args, **kwargs):
        user = request.user
        return super(BlogPostList, self).get(...)


// serializers.py
class BlogPostSerializer(serializer.ModelSerializer):
    user = someUserSerializer(read_only=True)
    likes = BlogPostLikesSerializer()
    class Meta:
        model = BlogPost
        fields = ("id", "user", "likes", "content", "image")

class BlogPostLikesSerializer(serializer.ModelSerializer):
    blogpost = BlogPostSerializer()
    user = SomeUserSerializer()
    class Meta:
        model = BlogPostLikes
        fields = ("id", "user", "blogpost", "timestamp")

但后来我卡住了。我不知道如何修改我的 BlogPostLikesSerializer 以指示当前用户(来自 views.pyrequest.user)是否包含在博客帖子的点赞集中。你们知道我怎样才能做到这一点吗?

谢谢

您可以使用 SerializerMethodField 动态获取标志,如下所示:

class BlogPostSerializer(serializer.ModelSerializer):
    user = SomeUserSerializer()
    likes = BlogPostLikesSerializer()
    has_liked = SerializerMethodField("get_has_liked")
    class Meta:
        model = BlogPostLikes
        fields = ("id", "user", "likes", "content", "image", "has_liked")

    def get_has_liked(self, obj):
        user = self.context.get('request').user
        return len([l.user.id == user.id for l in obj.likes]) > 0

但是,您需要在实例化序列化程序时传递请求,方法如下:serializer = BlogPostSerializer(context={'request': request})

我可以想到两种方法来实现这一点。

  1. 如果你只需要booleanTrue/False是否Postrequest.user喜欢,你可以使用 .extra.

    1.1。将您的 BlogPost Queryset 更改为如下内容:

    class BlogPostQuerySet(models.QuerySet):
        def annotate_is_liked_by_user(self, user):
            return self.extra(
                select = {'is_liked': 'EXISTS( \
                    SELECT `id` FROM `blogpostlikes` \
                    WHERE `blogpostlikes`.`blogpost_id` = `blogpost`.id \
                    AND `blogpostlikes`.`user_id` = %s)' % user.id
                }
            )
    
    class BlogPost
        # other stuffs here
        objects = BlogPostQuerySet.as_manager()
    

    1.2。在 BlogPostList 视图

    中更改 get_queryset 方法
    class BlogPostList(ListAPIView):
    
        def get_queryset(self):
            user = self.request.user
            return BlogPost.objects.annotate_is_liked_by_user(user)
    

    1.3。将新字段添加到 BlogPostSerializer

    class BlogPostSerializer(serializer.ModelSerializer):
        # ....
        is_liked = serializers.BooleanField(source='is_liked')
        # ...
    
  2. 获取整个 BlogPostLike 对象。

    2.1.Alter get_queryset 方法。

    class BlogPostList(ListAPIView):
    
        def get_queryset(self):
            user = self.request.user
            return BlogPost.objects.prefetch_related(
                Prefetch(
                    'likes', 
                    queryset=BlogPostLikes.objects.filter(user=user) \
                        .select_related('user'), 
                    to_attr='likes_by_request_user'
                )
            )
    

    2.2。改变 serializer。但这可以通过两种方式完成:

    2.2.1。序列化其中包含一项的列表:

    class BlogPostSerializer(serializer.ModelSerializer):
        # ...
        likes_by_request_user = BlogPostLikesSerializer(many=True)
    

    并从 BlogPostLikesSerializer 中删除 blogpost = BlogPostSerializer()。我认为这会导致无限循环。

    2.2.2 序列化单个对象:

    class BlogPostSerializer(serializer.ModelSerializer):
        # ...
        like_by_request_user = BlogPostLikesSerializer(source='get_last_like', required=False)
    

    但是这样你将不得不在你的 BlogPost 对象中添加一个新方法到 return 这个单一的 Like 对象

    class BlogPost(models.Model):
        #...
        def get_last_like(self):
            if hasattr(self, 'likes_by_request_user') and len(self.likes_by_request_user) > 0:
                return self.likes_by_request_user[0]
            return None