2013-10-19 70 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() 
相關問題