2012-08-24 4 views
8

Nella seguente configurazione, vorrei un QuerySet con un elenco di progetti, ciascuno annotato con la somma di tutte le durate delle attività (come tasks_duration) e la somma di tutte le sue attività ' sottotask durations (come subtasks_duration). I miei modelli (semplificato) simile a questa:annotazione multipla I termini di somma danno una risposta gonfiata

class Project(models.Model): 
    pass 

class Task(models.Model): 
    project = models.ForeignKey(Project) 
    duration = models.IntegerField(blank=True, null=True) 

class SubTask(models.Model): 
    task = models.ForeignKey(Task) 
    duration = models.IntegerField(blank=True, null=True) 

Faccio il mio QuerySet in questo modo:

Projects.objects.annotate(tasks_duration=Sum('task__duration'), subtasks_duration=Sum('task__subtask__duration')) 

correlati al comportamento spiegato in Django annotate() multiple times causes wrong answers ho un tasks_duration che è molto più alto di quanto dovrebbe essere. Le clausole di annotazione multipla (Sum()) producono più join interni a sinistra nell'SQL risultante. Con un solo termine annotazione (Sum()) per tasks_duration, il risultato è corretto. Tuttavia, mi piacerebbe avere sia tasks_duration sia subtasks_duration.

Quale sarebbe un modo adeguato per eseguire questa query? Ho una soluzione funzionante che lo fa per progetto, ma è prevedibilmente lento. Ho anche qualcosa di simile a lavorare con una chiamata extra(), ma mi piacerebbe davvero sapere se ciò che voglio è possibile con Django puro.

+0

Hai provato 'tasks_duration = Sum ('task__duration', distinta = True), subtasks_duration = Somma ('task__subtask__duration', distinta = True)' come menzionato nell'altra domanda che hai collegato? – jpic

+0

Questo servirà solo per sommare valori di durata distinti, che non è quello che voglio. Per curiosità, l'ho provato, restituisce sempre gli stessi valori errati. (le durate variano) –

+0

Hai mai trovato una soluzione per questo? Sto cercando di annotare una somma e un conteggio su un queryset, e la somma continua a moltiplicarsi ... – StephenTG

risposta

1

Ottengo anche questo errore. Esatto stesso codice. Funziona se faccio l'aggregazione separatamente, ma una volta che cerco di ottenere entrambe le somme allo stesso tempo, uno di loro ottiene un fattore 2 più alto e l'altro un fattore 3.

Non ho idea del perché Django si comporta in questo modo modo. Ho inoltrato una segnalazione di errore qui: https://code.djangoproject.com/ticket/19011 Potrebbe interessarti anche seguirlo.

+0

Non è proprio un bug in Django, ma un problema della complessità nel fare queste query aggregate al database. Anche se hai scritto tu stesso l'SQL, dovresti risolvere questo problema. – benjaoming

+1

Link al rapporto bug pertinente: https://code.djangoproject.com/ticket/10060 – benjaoming

+0

Questo bug non è ancora stato corretto. – sobolevn

1

Il bug è stato segnalato here ma non è stato risolto nemmeno in Django 1.11. Il problema è legato all'unione di due tabelle in relazioni inverse. Si noti che il parametro distinto funziona bene per Conteggio ma non per Somma. Così si può usare un trucco e scrivere un ORM come di seguito:

Projects.objects.annotate(
     temp_tasks_duration=Sum('task__duration'), 
     temp_subtasks_duration=Sum('task__subtask__duration'), 
     tasks_count=Count('task'), 
     tasks_count_distinct=Count('task', distinct=True), 
     task_subtasks_count=Count('task__subtask'), 
     task_subtasks_count_distinct=Count('task__subtask', distinct=True), 
).annotate(
     tasks_duration=F('temp_tasks_duration')*F('tasks_count_distinct')/F('tasks_count'), 
     subtasks_duration=F('temp_subtasks_duration')*F('subtasks_count_distinct')/F('subtasks_count'), 
)