2015-08-10 9 views
19

Próbuję znaleźć najlepszy sposób dodawania pól z adnotacjami, takich jak wszelkie zagregowane (obliczone) pola do serializatorów DRF (Model). Mój przypadek użycia to po prostu sytuacja, w której punkt końcowy zwraca pola, które NIE są przechowywane w bazie danych, ale są obliczane z bazy danych.Agregowane (i inne adnotowane) pola w serializacjach Django Rest Framework

Spójrzmy na poniższy przykład:

models.py

class IceCreamCompany(models.Model): 
    name = models.CharField(primary_key = True, max_length = 255) 

class IceCreamTruck(models.Model): 
    company = models.ForeignKey('IceCreamCompany', related_name='trucks') 
    capacity = models.IntegerField() 

serializers.py

class IceCreamCompanySerializer(serializers.ModelSerializer): 
    class Meta: 
     model = IceCreamCompany 

pożądane wyjście JSON:

[ 

    { 
     "name": "Pete's Ice Cream", 
     "total_trucks": 20, 
     "total_capacity": 4000 
    }, 
    ... 
] 

Mam kilka rozwiązania th w pracy, ale każdy ma pewne problemy.

Opcja 1: dodanie pobierające do modelowania i wykorzystania SerializerMethodFields

models.py

class IceCreamCompany(models.Model): 
    name = models.CharField(primary_key=True, max_length=255) 

    def get_total_trucks(self): 
     return self.trucks.count() 

    def get_total_capacity(self): 
     return self.trucks.aggregate(Sum('capacity'))['capacity__sum'] 

serializers.py

class IceCreamCompanySerializer(serializers.ModelSerializer): 

    def get_total_trucks(self, obj): 
     return obj.get_total_trucks 

    def get_total_capacity(self, obj): 
     return obj.get_total_capacity 

    total_trucks = SerializerMethodField() 
    total_capacity = SerializerMethodField() 

    class Meta: 
     model = IceCreamCompany 
     fields = ('name', 'total_trucks', 'total_capacity') 

Powyższy kod może być może trochę refactored, ale to nie zmieni faktu, że ta opcja wykona 2 dodatkowe zapytania SQL za IceCreamCompany który nie jest bardzo wydajny.

Opcja 2: opisywanie w ViewSet.get_queryset

models.py jak pierwotnie opisane.

views.py

class IceCreamCompanyViewSet(viewsets.ModelViewSet): 
    queryset = IceCreamCompany.objects.all() 
    serializer_class = IceCreamCompanySerializer 

    def get_queryset(self): 
     return IceCreamCompany.objects.annotate(
      total_trucks = Count('trucks'), 
      total_capacity = Sum('trucks__capacity') 
     ) 

To będzie zagregowane pól w jednym zapytaniu SQL, ale nie jestem pewien, jak chciałbym dodać je do serializer jak DRF nie magicznie wiedzieć, że mam opatrzone adnotacjami te pola w QuerySet. Jeśli dodaję total_trucks i total_capacity do serializera, wyświetli się błąd dotyczący braku tych pól w Modelu.

Opcja 2 może być wykonana bez serializatora za pomocą View, ale jeśli model zawiera dużo pól, a tylko niektóre z nich muszą znajdować się w JSON, to byłoby trochę brzydkim hackem do zbudowania punktu końcowego bez serializer.

Odpowiedz

25

Możliwe rozwiązanie:

views.py

class IceCreamCompanyViewSet(viewsets.ModelViewSet): 
    queryset = IceCreamCompany.objects.all() 
    serializer_class = IceCreamCompanySerializer 

    def get_queryset(self): 
     return IceCreamCompany.objects.annotate(
      total_trucks=Count('trucks'), 
      total_capacity=Sum('trucks__capacity') 
     ) 

serializers.py

class IceCreamCompanySerializer(serializers.ModelSerializer): 
    total_trucks = serializers.IntegerField() 
    total_capacity = serializers.IntegerField() 

    class Meta: 
     model = IceCreamCompany 
     fields = ('name', 'total_trucks', 'total_capacity') 

Korzystając Serializer fields Mam mały przykład do pracy. Pola muszą być zadeklarowane jako atrybuty klasy serializera, aby DRF nie zgłaszał błędów o nich nieistniejących w modelu IceCreamCompany.

+0

To zalecane rozwiązanie. DRF nie może introspekować pól, więc musisz je określić ręcznie. –

+0

Miałem nadzieję na rozwiązanie, w którym mógłbym zmodyfikować zestaw zapytań w serializatorze, zanim SQL faktycznie odpali. Czy nie ma sensu w cyklu życia serializera, w którym mógłbym wstawić hak do adnotacji? Byłoby miło móc serializować dowolny zestaw zapytań wbudowany w model 'IceCreamCompany' bez konieczności ręcznego dodawania adnotacji ... – Coderer

+0

Próbuję osiągnąć ten sam wynik, ale nie mogę sobie wyobrazić, jak korzystać z ViewSet. Czy możesz szczegółowo opisać, w jaki sposób IceCreamCompanyViewSet jest następnie używany w twoim (lub teoretycznym) kodzie? –