Considere modelos simples de Django Eventy Participant:
class Event(models.Model):
title = models.CharField(max_length=100)
class Participant(models.Model):
event = models.ForeignKey(Event, db_index=True)
is_paid = models.BooleanField(default=False, db_index=True)
Es fácil anotar consultas de eventos con el número total de participantes:
events = Event.objects.all().annotate(participants=models.Count('participant'))
¿Cómo anotar con el recuento de participantes filtrados por is_paid=True?
Necesito consultar todos los eventos independientemente del número de participantes, por ejemplo, no necesito filtrar por resultado anotado. Si hay 0participantes, está bien, solo necesito0 valor anotado.
El ejemplo de la documentación no funciona aquí, porque excluye objetos de la consulta en lugar de anotarlos con 0.
Actualizar. Django 1.8 tiene una nueva función de expresiones condicionales , por lo que ahora podemos hacer lo siguiente:
events = Event.objects.all().annotate(paid_participants=models.Sum(
models.Case(
models.When(participant__is_paid=True, then=1),
default=0,
output_field=models.IntegerField()
)))
Actualización 2. Django 2.0 tiene una nueva función de agregación condicional , consulte la respuesta aceptada a continuación.

aggregatese muestra el uso. ¿Ya ha probado este tipo de consultas? (¡No lo he hecho y quiero creer! :)Acabo de descubrir que Django 1.8 tiene una nueva función de expresiones condicionales , así que ahora podemos hacer lo siguiente:
fuente
Count(en lugar deSum), supongo que deberíamos establecerdefault=None(si no usamos elfilterargumento django 2 ).ACTUALIZAR
El enfoque de subconsulta que menciono ahora es compatible con Django 1.11 a través de subconsultas-expresiones .
Prefiero esto a la agregación (suma + caso) , porque debería ser más rápido y fácil de optimizar (con la indexación adecuada) .
Para la versión anterior, se puede lograr lo mismo usando
.extrafuente
.extra, ya que prefiero evitar SQL en Django :) Actualizaré la pregunta.Django 1.8.2, así que supongo que estás con esa versión y es por eso que funciona para ti. Puede leer más sobre eso aquí y aquíNonetambién. Mi solución fue usarCoalesce(from django.db.models.functions import Coalesce). Se utiliza de esta manera:Coalesce(Subquery(...), 0). Sin embargo, puede haber un mejor enfoque.Sugeriría utilizar el
.valuesmétodo de suParticipantconsultas en lugar.En resumen, lo que quieres hacer viene dado por:
Un ejemplo completo es el siguiente:
Crear 2
Events:Agregue
Participants a ellos:Agrupe todos
Participantlos correos electrónicos por sueventcampo:Aquí se necesita distinto:
Lo que
.valuesy.distinctestán haciendo aquí es que están creando dos cubos deParticipants agrupados por su elementoevent. Tenga en cuenta que esos cubos contienenParticipant.Luego puede anotar esos depósitos, ya que contienen el conjunto de originales
Participant. Aquí queremos contar el número deParticipant, esto se hace simplemente contando losids de los elementos en esos cubos (ya que sonParticipant):Finalmente, solo desea
Participantcon unis_paidserTrue, puede agregar un filtro delante de la expresión anterior, y esto produce la expresión que se muestra arriba:El único inconveniente es que debe recuperar el
Eventdespués, ya que solo tiene eliddel método anterior.fuente
Qué resultado estoy buscando:
En general, tendría que usar dos consultas diferentes:
Pero quiero ambos en una consulta. Por lo tanto:
Resultado:
fuente