update - Campos agregados(y otros campos anotados) en los serializadores Django Rest Framework
django rest framework with django 2 (1)
Solución posible:
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'')
Al usar los campos del serializador obtuve un pequeño ejemplo para trabajar. Los campos deben declararse como atributos de clase del serializador para que DRF no arroje un error sobre ellos que no existan en el modelo IceCreamCompany.
Estoy tratando de encontrar la mejor manera de agregar campos anotados, como cualquier campo agregado (calculado) a Serializadores DRF (Modelo). Mi caso de uso es simplemente una situación en la que un punto final devuelve campos que NO están almacenados en una base de datos, sino que se calculan a partir de una base de datos.
Veamos el siguiente ejemplo:
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
salida JSON deseada:
[
{
"name": "Pete''s Ice Cream",
"total_trucks": 20,
"total_capacity": 4000
},
...
]
Tengo un par de soluciones que funcionan, pero cada una tiene algunos problemas.
Opción 1: agregar getters para modelar y usar 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'')
El código anterior quizás se pueda refactorizar un poco, pero no cambiará el hecho de que esta opción realizará 2 consultas SQL adicionales por IceCreamCompany que no es muy eficiente.
Opción 2: anotar en ViewSet.get_queryset
models.py como se describió originalmente.
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'')
)
Esto obtendrá los campos agregados en una única consulta SQL, pero no estoy seguro de cómo los agregaría al Serializador ya que DRF no sabe mágicamente que he anotado estos campos en QuerySet. Si agrego total_trucks y total_capacity al serializador, arrojará un error sobre estos campos que no están presentes en el modelo.
La opción 2 puede funcionar sin un serializador utilizando una vista, pero si el modelo contiene muchos campos, y solo algunos deben estar en el JSON, sería un truco algo feo construir el punto final sin un serializador.