2015-05-04 7 views
9

Ho un modello di argomento del forum che desidero ordinare su un SerializerMethodField calcolato, ad esempio vote_count. Qui ci sono un modello molto semplificato, Serializer e ViewSet per mostrare il problema:Ordine di restituzione di Django Framework su un SerializerMethodField

# models.py 
class Topic(models.Model): 
    """ 
    An individual discussion post in the forum 
    """ 
    title = models.CharField(max_length=60) 

    def vote_count(self): 
     """ 
     count the votes for the object 
     """ 
     return TopicVote.objects.filter(topic=self).count() 


# serializers.py 
class TopicSerializer(serializers.ModelSerializer): 
    vote_count = serializers.SerializerMethodField() 

    def get_vote_count(self, obj): 
     return obj.vote_count() 

    class Meta: 
     model = Topic 


# views.py 
class TopicViewSet(TopicMixin, viewsets.ModelViewSet): 
    queryset = Topic.objects.all() 
    serializer_class = TopicSerializer 

Ecco cosa funziona:

  1. OrderingFilter è attivata per impostazione predefinita e posso ordinare con successo /topics?ordering=title
  2. La funzione vote_count funziona perfettamente

Sto cercando di ordinare dal MethodField su TopicSerializer, vote_count come /topics?ordering=-vote_count ma sembra che non sia supportato. C'è un modo che posso ordinare da quel campo?

La mia risposta semplificata JSON è simile al seguente:

{ 
    "id": 1, 
    "title": "first post", 
    "voteCount": 1 
}, 
{ 
    "id": 2, 
    "title": "second post", 
    "voteCount": 8 
}, 
{ 
    "id": 3, 
    "title": "third post", 
    "voteCount": 4 
} 

sto usando Ember a consumare il mio API e il parser si sta trasformando a camelCase. Ho provato l'ordinazione = voteCount pure, ma che non funziona (e non dovrebbe)

risposta

18

questo non è possibile utilizzando the default OrderingFilter, perché l'ordinamento è implementato sul lato del database. Questo è per motivi di efficienza, poiché l'ordinamento manuale dei risultati può essere incredibilmente lento e significa rompere da uno standard QuerySet. Mantenendo tutto come QuerySet, si beneficia del filtro incorporato fornito da framework Django REST (che generalmente si aspetta un QuerySet) e dall'impaginazione integrata (che può essere lenta senza uno).

Ora avete due opzioni in questi casi: capire come recuperare il valore sul lato del database, o cercare di minimizzare il colpo di prestazioni che dovrete prendere. Dato che quest'ultima opzione è molto specifica per l'implementazione, per ora la salterò.

In questo caso, è possibile utilizzare the Count function fornito da Django per eseguire il conteggio sul lato del database. Viene fornito come parte di the aggregation API e funziona come the SQL COUNT function. Si può fare l'equivalente Count chiamata modificando il queryset sulla vista di essere

queryset = Topic.objects.annotate(vote_count=Count('topicvote_set')) 

Sostituzione topicvote_set con your related_name for the field (si dispone di un set, giusto?). Ciò ti consentirà di ordinare i risultati in base al numero di voti e persino di filtrare (se lo desideri) perché è disponibile all'interno della query stessa.

Ciò richiederebbe una leggera modifica al serializzatore, quindi viene estratta dalla nuova proprietà vote_count disponibile sugli oggetti.

class TopicSerializer(serializers.ModelSerializer): 
    vote_count = serializers.IntegerField(read_only=True) 

    class Meta: 
     model = Topic 

Questo avrà la precedenza il metodo esistente vote_count, quindi si consiglia di rinominare la variabile utilizzata durante l'annotazione (se non è possibile sostituire il vecchio metodo).


Inoltre, è possibile passare un nome di metodo come il source di un campo framework Django REST e sarà automaticamente chiamarlo.Quindi, tecnicamente il vostro serializzatore corrente potrebbe essere solo

class TopicSerializer(serializers.ModelSerializer): 
    vote_count = serializers.IntegerField(read_only=True) 

    class Meta: 
     model = Topic 

E che avrebbe funzionato esattamente come avviene attualmente. Nota che in questo caso è richiesto read_only perché un metodo non è uguale a una proprietà, quindi il valore non può essere impostato.

+0

ottima risposta, gli darei 2 voti se potessi! Funziona perfettamente e grazie per l'ultimo consiglio su come sbarazzarsi del MethodField. – awwester

Problemi correlati