2010-06-28 12 views
5

Ho cercato dappertutto per una risposta a questo, ma non ho trovato nulla. Forse questa è solo una domanda stupida o davvero difficile. Eccolo:Annotare un set di query con la differenza di data media? (django)

Diciamo che il mio modello è questo (pseudo codice django):

Event 
    type = ForeignKey(EventType) 
    name = CharField 
    date_start = DateField 
    date_end = DateField 

EventType 
    name = CharField 

Quello che voglio sapere è il tempo medio di durata per ogni tipo di evento. Quello che faccio ora è calcolare la durata media ogni volta che viene creato un nuovo evento (metodo di salvataggio) e memorizzarlo in una colonna average_duration in EventType. Il problema con questo approccio è che non posso rispondere a domande come "qual è stata la durata media per gli eventi di tipo X, durante l'anno Y". Quindi, invece di aggiungere più colonne per rispondere a domande come queste, preferirei farlo in "tempo reale".

Questo può essere fatto annotando il queryset? Per prima cosa dovrei ottenere le differenze di data per ogni tipo di evento, quindi ottenere la loro media e quindi annotare il queryset dell'evento con quella media, presumo.

risposta

1

Credo che la soluzione migliore sia creare una vista SQL con la colonna date_end - date_start, creare un modello django su questa vista e quindi sarà possibile interrogare la vista e annotarla come si desidera. Ho fatto questo con modelli simili ai tuoi e forse potrei estrarre un codice interessante per te se ne hai bisogno.

+0

Sì se si potrebbe fornire alcuni esempi di codice, sarebbe fantastico, grazie –

1

Avrai bisogno di creare un set di query con il metodo extra di aggiungere la differenza data per ogni riga

quindi utilizzare il metodo aggregate per calcolare la media per la colonna appena aggiunto:

Attenzione però , questo metodo è lento e non verrà ridimensionato. Memorizzare il valore calcolato su event_type è imho la migliore opzione.

2

vorrei suggerire che si memorizza la durata dell'evento come una colonna:

event_duration = models.IntegerField() 
... 

def __init__(self, *args, **kwargs): 
    super(Event, self).__init__(*args, **kwargs) 
    self.update_event_duration() 


def save(self, **kwargs): 
    self.update_event_duration() 
    super(Event, self).save(*args, **kwargs) 

Quindi, è possibile utilizzare questa libreria: http://code.google.com/p/django-cube/ per calcolare la media su più dimensioni diverse (anno, tipo, o altro):

>>> def my_avg(queryset): 
... return queryset.aggregate(Avg("event_duration"))["event_duration__avg"] 

>>> c = Cube(["date_start__year", "type"], Event.objects.all(), my_avg) 

Utilizzare il cubo in questo modo:

>>> c.measure(date_start__year=1999, type=event_type2) 
123.456 

Oppure si può ottenere tutte le medie su tutti gli anni:

>>> c.measure_dict("date_start__year") 
{1984: {'measure': 111.789}, 1985: {'measure': 234.666}, ...} 

o per tipo di anno/evento:

>>> c.measure_dict("date_start__year", "type") 
{ 
    1984: {eventtype1: {'measure': 111.789}, eventtype2: {'measure': 234.666}, ...}, 
    1985: {eventtype1: {'measure': 122.79}, eventtype2: {'measure': 233.444}, ...}, 
    ... 
} 
+0

Non lo so ma questa soluzione sembra spezzare l'ORM per calcolare un valore, tuttavia, penso che Django non lo supporti ancora. –

18

Solo un aggiornamento. In Django 1.8 è possibile fare:

from django.db.models import F, ExpressionWrapper, fields 

duration = ExpressionWrapper(F('date_end') - F('date_start'), output_field=fields.DurationField()) 

events_with_duration = Event.objects.annotate(duration=duration) 

dopo il quale è possibile eseguire le query come:

events_with_duration.filter(duration_gt=timedelta(days=10)) 
+1

Non dovrebbe essere date_end - date_start? –

+0

@AndreBossard hai assolutamente ragione. grazie – ryuusenshi

+1

Test in Django 1.8, questo non sembra funzionare quando applicato alle funzioni di aggregazione (ad esempio Event.objects.annotate (duration = duration) .aggregrate (Avg ('duration'))) –

Problemi correlati