2013-10-19 29 views
12

我想添加一个字段到序列化程序,其中包含特定于发出当前请求的用户的信息(我不想为此创建单独的端点)。这里就是这样,我做到了:将用户特定的字段添加到Django REST Framework序列化器

的视图集:

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 

有没有更好的地方注入喜欢文章的预取,或一个更好的方法来做到这一点?

回答

8

我倾向于尝试尽可能多地将它放在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类方法可以封装您的预取(和缓存)的逻辑。

我希望有帮助。

+0

我喜欢的自定义字段,但'user_likes_article'将无法做太多的缓存/预取的方式,如果你做的是通过一个单一的文章。我在'get_queryset'内部预取的原因是所有与请求相关的文章ID都在那里是已知的。查询集以某种方式在序列化程序字段中可用? –

+0

我假设你会使用单篇文章参数来从(缓存的)user_likes集合(或这样的)中选择文章您的QuerySet只是Articles.objects.all()正确吗?这里没有任何具体的请求,但是你究竟如何实现user_likes_article会(当然)取决于你想要做什么。 –

+0

我意识到我原来的问题存在一个严重的错误;它并没有考虑过滤或分页。我现在修改了它,并且我认为它更有意义。您可以看到预取如何与请求紧密结合(过滤和分页),并且我不能轻易将它移动到Like模型中。 –

26

你可以用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}) 
+0

使用过滤器时,您不需要'尝试除了',否则,如果查询为空,则不会引发错误,只会返回空查询。而不是'count()',你可以直接使用'exists()'。 – Sassan

0

按照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() 
相关问题