2013-10-19 6 views
12

Ich möchte ein Feld zu einem Serializer hinzufügen, das spezifische Informationen für den Benutzer enthält, der die aktuelle Anfrage erstellt (ich möchte keinen separaten Endpunkt dafür erstellen). Hier ist die Art, wie ich es tue:Hinzufügen von benutzerspezifischen Feldern zum Django REST Framework-Serializer

Die Viewset:

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 

Der Serializer:

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 

Gibt es einen besseren Ort, um das Prefetching von gemocht Artikeln zu injizieren, oder Einen schöneren Weg, dies generell zu tun?

Antwort

8

Ich würde gerne versuchen, so viel wie möglich auf das Like Modellobjekt zu setzen und dann den Rest in ein benutzerdefiniertes Serializer-Feld zu stecken.

In Serializer Felder können Sie die request über den context Parameter zugreifen, die sie von ihren Eltern erben Serializer .

So könnten Sie so etwas tun:

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

Die user_likes_article Klasse Methode könnte dann kapseln Ihre Prefetching (und Caching) Logik.

Ich hoffe, dass hilft.

+0

Ich mag das benutzerdefinierte Feld, aber 'user_likes_article' würde im Weg Caching/Prefetching nicht in der Lage sein, viel zu tun, wenn alles, was Sie tun, gibt ihnen einen einzigen Artikel ist. Der Grund für den Prefetch in 'get_queryset' ist, dass dort alle für die Anfrage relevanten Artikel-IDs bekannt sind. Ist das Abfrage-Set in dem Serialisierungsfeld irgendwie verfügbar? –

+0

Ich nehme an, Sie würden den einzelnen Artikel-Parameter verwenden, um den Artikel aus einer (zwischengespeicherten) Sammlung von user_likes (oder so) auszuwählen. Ihr QuerySet ist nur Articles.objects.all() richtig? Es gibt dort keine spezifischen Anforderungen, aber genau wie Sie user_likes_article implementieren, hängt (natürlich) davon ab, was genau Sie tun möchten. –

+0

Ich erkannte, dass es in meiner ursprünglichen Frage einen ernsten Fehler gab; Die Filtersatz- oder Seitenumbruch-Abfrage wurde nicht berücksichtigt. Ich habe es jetzt geändert, und ich denke, es macht ein bisschen mehr Sinn. Sie können sehen, wie das Prefetching eng mit der Anfrage gekoppelt ist (Filterung und Paginierung) und dass ich es nicht einfach in das Like-Modell verschieben kann. –

26

Sie können es mit SerializerMethodField

Beispiel:

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 

dann sollten Sie Serializer aus Sicht wie folgt aufrufen:

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

Sie benötigen nicht "try except", wenn Sie einen Filter verwenden, es wird kein Fehler ausgegeben, wenn die Abfrage leer ist, Sie geben einfach eine leere Abfrage zurück. und anstelle von 'count()' können Sie 'exists()' direkt verwenden. – Sassan

0

Nach dem Django Documentation - SerializerMethodField musste ich Ändere den Code von rapid2share etwas.

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()