2013-10-19 12 views
12

Desidero aggiungere un campo a un serializzatore che contiene informazioni specifiche dell'utente che effettua la richiesta corrente (non voglio creare un endpoint separato per questo). Ecco il modo in cui l'ho fatto:Aggiungere campi specifici dell'utente al serializzatore Django REST Framework

Il 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 

Il serializzatore:

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 

C'è un posto migliore per iniettare il prefetching di articoli voluto, o un modo migliore per farlo in generale?

risposta

8

Sarei propenso a provare a mettere quanto più possibile possibile sull'oggetto modello Like e quindi applicare il resto in un campo serializzatore personalizzato.

Nei campi serializzatore è possibile accedere al request tramite il parametro context che ereditano dal loro serializzatore genitore.

Così si potrebbe fare qualcosa di simile:

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

Il metodo user_likes_article classe potrebbe quindi incapsulare la logica di prefetching (e caching).

Spero che questo aiuti.

+0

Mi piace il campo personalizzato, ma 'user_likes_article' non sarebbe in grado di fare molto in termini di caching/prefetching se tutto ciò che fai è passarlo un unico articolo. La ragione per cui ho il prefetching all'interno di 'get_queryset' è che tutti gli id ​​degli articoli rilevanti per la richiesta sono noti lì. Il queryset è in qualche modo disponibile nel campo del serializzatore? –

+0

Suppongo che useresti il ​​parametro articolo singolo per scegliere l'articolo da una raccolta (memorizzata nella cache) di user_likes (o simili). Il tuo QuerySet è solo Articles.objects.all() giusto? Non c'è nulla di specifico, ma esattamente come implementate user_likes_article (ovviamente) dipenderà esattamente da cosa state provando a fare. –

+0

Mi sono reso conto che c'era un bug grave nella mia domanda iniziale; non ha rispettato il filtraggio o l'impaginazione del queryset. L'ho modificato ora, e penso che abbia un po 'più senso. Puoi vedere come il prefetching è strettamente associato alla richiesta (filtraggio e impaginazione) e che non posso spostarlo facilmente nel modello Like. –

26

si può fare con SerializerMethodField

Esempio:

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 

allora si dovrebbe chiamare serializzatore alla vista in questo modo:

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

Non è necessario "provare tranne" quando si utilizza il filtro, non genera un errore se la query è vuota, restituisce semplicemente una query vuota. e invece di 'count()' puoi usare 'exists()' direttamente. – Sassan

0

Secondo il Django Documentation - SerializerMethodField, ho dovuto cambia leggermente il codice di 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() 
Problemi correlati