【问题标题】:Add user specific fields to Django REST Framework serializer将用户特定字段添加到 Django REST Framework 序列化程序
【发布时间】:2013-10-19 16:47:00
【问题描述】:

我想向序列化程序添加一个字段,其中包含特定于发出当前请求的用户的信息(我不想为此创建单独的端点)。这是我的做法:

视图集:

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    filter_class = ArticleFilterSet

    def prefetch_likes(self, ids):
        self.current_user_likes = dict([(like.article_id, like.pk) for like in Like.objects.filter(user=self.request.user, article_id__in=ids)])

    def get_object(self, queryset=None):
        article = super(ArticleViewSet, self).get_object(queryset)
        self.prefetch_likes([article.pk])
        return article

    def paginate_queryset(self, queryset, page_size=None):
        page = super(ArticleViewSet, self).paginate_queryset(queryset, page_size)
        if page is None:
            return None

        ids = [article.pk for article in page.object_list]
        self.prefetch_likes(ids)

        return page

序列化器:

class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article

    def to_native(self, obj):
        ret = super(ArticleSerializer, self).to_native(obj)

        if obj:
            view = self.context['view']
            ret['has_liked'] = False
            if hasattr(view, 'current_user_liked'):
                ret['has_liked'] = obj.pk in view.current_user_liked

        return ret

有没有更好的地方来注入喜欢文章的预取,或者有更好的方法来做这件事?

【问题讨论】:

    标签: python django django-rest-framework


    【解决方案1】:

    你可以用SerializerMethodField做到这一点

    示例:

    class PostSerializer(serializers.ModelSerializer):
        fav = serializers.SerializerMethodField('likedByUser')
    
        def likedByUser(self, obj):
            request = self.context.get('request', None)
            if request is not None:
                try:
                    liked=Favorite.objects.filter(user=request.user, post=obj.id).count()
                    return liked == 1
                except Favorite.DoesNotExist:
                    return False
            return "error"
    
        class Meta:
            model = Post
    

    那么你应该像这样从视图中调用序列化程序:

    class PostView(APIVIEW):
         def get(self,request):
             serializers = PostSerializer(PostObjects,context={'request':request})
    

    【讨论】:

    • 使用过滤器时不需要try except,如果查询为空,它不会引发错误,只是返回一个空查询。而不是count(),您可以直接使用exists()
    【解决方案2】:

    根据Django Documentation - SerializerMethodField,我不得不稍微更改rapid2share的代码。

    class ResourceSerializer(serializers.ModelSerializer):
        liked_by_user = serializers.SerializerMethodField()
    
        def get_liked_by_user(self, obj : Resource):
            request = self.context.get('request')
            return request is not None and obj.likes.filter(user=request.user).exists()
    

    【讨论】:

      【解决方案3】:

      我倾向于尝试将尽可能多的内容放在Like 模型对象上,然后将其余部分放入自定义序列化程序字段中。

      在序列化器字段中,您可以通过它们从父序列化器继承context 参数访问request

      所以你可能会这样做:

      class LikedByUserField(Field):
          def to_native(self, article):
              request = self.context.get('request', None)
              return Like.user_likes_article(request.user, article)
      

      user_likes_article 类方法可以封装您的预取(和缓存)逻辑。

      希望对你有帮助。

      【讨论】:

      • 我喜欢自定义字段,但user_likes_article 将无法在缓存/预取方面做很多事情,如果你只是传递一篇文章。我在get_queryset 中进行预取的原因是所有与请求相关的文章 ID 都在那里已知。查询集是否以某种方式在序列化器字段中可用?
      • 我假设您会使用单个文章参数从 user_likes(或此类)的(缓存)集合中选择文章您的 QuerySet 只是 Articles.objects.all() 对吗?那里没有具体的请求,但您实现 user_likes_article 的具体方式(当然)取决于您正在尝试做什么。
      • 我意识到我原来的问题中有一个严重的错误;它根本不尊重查询集过滤或分页。我现在修改了它,我认为它更有意义。您可以看到预取是如何与请求(过滤和分页)紧密耦合的,并且我无法轻易将其移至 Like 模型中。
      • @kajic 好的,问题略有变化,但我认为答案并非如此——当然它背后的原理并没有。假设用户可能在每个会话中请求多个页面并且您的数据不是太大™,只需缓存 user_likes 并检查给定的文章 ID。如果您的数据最终变得太大,那么您就是一个快乐的人,然后您可以做一些更聪明的事情。
      猜你喜欢
      • 2016-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-16
      相关资源
      最近更新 更多