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.
To zalecane rozwiązanie. DRF nie może introspekować pól, więc musisz je określić ręcznie. –
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
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? –