2015-05-04 5 views
9

Mam model forum, który chcę zamówić na obliczonej SerializerMethodField, takiej jak vote_count. Oto bardzo uproszczony model, serializer i ViewSet pokazać problem:Django Rest Framework Zamawianie na 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 

Oto, co działa:

  1. OrderingFilter jest domyślnie włączona i powodzeniem mogę zamówić /topics?ordering=title
  2. Funkcja vote_count działa idealnie

Próbuję zamówić przez MethodField na TopicSerializer, vote_count jak /topics?ordering=-vote_count, ale wygląda na to, że nie jest obsługiwane. Czy istnieje sposób, w jaki mogę zamówić przez to pole?

Moja uproszczona odpowiedź JSON wygląda następująco:

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

Używam Ember zużywają moje API i parser zamienia go CamelCase. Próbowałem zamawiania = voteCount jak dobrze, ale to nie działa (i nie powinien)

Odpowiedz

18

To nie jest możliwe przy użyciu the default OrderingFilter, ponieważ kolejność jest realizowany po stronie bazy. Dzieje się tak ze względów związanych z wydajnością, ponieważ ręczne sortowanie wyników może być powolne i oznacza przerwanie ze standardu. Zachowując wszystko jako QuerySet, możesz korzystać z wbudowanego filtrowania zapewnianego przez framework RMS Django (który ogólnie oczekuje QuerySet) i wbudowanej paginacji (która może być wolna bez niej).

Teraz masz dwie opcje w tych przypadkach: dowiedz się, jak odzyskać swoją wartość po stronie bazy danych, lub spróbuj zminimalizować efekt wydajności, który musisz wykonać. Ponieważ ta ostatnia opcja jest bardzo specyficzna dla implementacji, na razie pominę to.

W tym przypadku można użyć numeru the Count function dostarczonego przez Django, aby wykonać obliczenie po stronie bazy danych. Jest to dostarczane jako część the aggregation API i działa jak the SQL COUNT function. Można zrobić równoważne Count połączenia przez zmodyfikowanie queryset na celu być

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

Wymiana topicvote_set z your related_name for the field (masz jeden zestaw, prawda?). Umożliwi to zamówienie wyników na podstawie liczby głosów, a nawet filtrowanie (jeśli chcesz), ponieważ jest dostępne w samym zapytaniu.

Wymagałoby to wprowadzenia niewielkich zmian w module serializacyjnym, dlatego jest pobierana z nowej właściwości vote_count dostępnej dla obiektów.

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

    class Meta: 
     model = Topic 

ten zastąpi istniejącą vote_count sposób, więc może chcesz zmienić nazwę zmiennej używaną podczas nanoszenia (jeśli nie można zastąpić starą metodę).


Można również przekazać nazwę metody jako source pola ramowej Django spoczynku i będzie automatycznie nazwać.Więc technicznie aktualna serializer może być tylko

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

    class Meta: 
     model = Topic 

I to działa dokładnie jak robi to obecnie. Zauważ, że w tym przypadku wymagana jest read_only, ponieważ metoda nie jest taka sama jak właściwość, więc nie można ustawić wartości.

+0

wspaniała odpowiedź, dałbym mu 2 głosy, gdybym mógł! Działa doskonale i dziękuje za ostatnią wskazówkę o pozbyciu się Metodego Pola. – awwester