2013-10-19 25 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

フィルタを使用するときに 'try except'を実行する必要はありません。クエリが空の場合はエラーを発生せず、単に空のクエリを返します。 'count()'の代わりに 'exist()'を直接使うことができます。 – 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() 
関連する問題