Purtroppo non c'è modo (che io sappia .. ho guardato abbastanza duro) per evitare di utilizzare un po ' tipo di raw sql per realizzare ciò che si desidera fare (con il modello corrente; vedere la fine per un altro suggerimento). Ma puoi minimizzare l'impatto scrivendo il più piccolo sql raw possibile. In pratica i siti django non hanno bisogno di essere trasferiti su database diversi. A meno che non preveda di utilizzare questa app altrove o di pubblicarla pubblicamente, dovresti essere ok.
L'esempio seguente è per sqlite. È possibile mantenere una mappatura dei tipi di database sulle funzioni date
, cercare il tipo di driver e sostituire la funzione con quella corretta se necessario.
>>> for stat in Stats.objects.all():
... print stat.created, stat.growth
...
2013-06-22 13:41:25.334262+00:00 3
2013-06-22 13:41:40.473373+00:00 3
2013-06-22 13:41:44.921247+00:00 4
2013-06-22 13:41:47.533102+00:00 5
2013-06-23 13:41:58.458250+00:00 6
2013-06-23 13:42:01.282702+00:00 3
2013-06-23 13:42:03.633236+00:00 1
>>> last_stat_per_day = Stats.objects.extra(
select={'the_date': 'date(created)' }
).values_list('the_date').annotate(max_date=Max('created'))
>>> last_stat_per_day
[(u'2013-06-22', datetime.datetime(2013, 6, 22, 13, 41, 47, 533102, tzinfo=<UTC>)), (u'2013-06-23', datetime.datetime(2013, 6, 23, 13, 42, 3, 633236, tzinfo=<UTC>))]
>>> max_dates = [item[1] for item in last_stat_per_day]
>>> max_dates
[datetime.datetime(2013, 6, 22, 13, 41, 47, 533102, tzinfo=<UTC>),
datetime.datetime(2013, 6, 23, 13, 42, 3, 633236, tzinfo=<UTC>)]
>>> stats = Stats.objects.filter(created__in=max_dates)
>>> for stat in stats:
... print stat.created, stat.growth
...
2013-06-22 13:41:47.533102+00:00 5
2013-06-23 13:42:03.633236+00:00 1
avevo scritto qui prima che questa era solo una singola query, ma ho mentito - il values_list deve essere trasformato per restituire solo il MAX_DATE per la query successiva, il che significa eseguire l'istruzione. Tuttavia sono solo 2 query, che sarebbero significativamente migliori di una funzione N + 1.
Il bit non-portatile è questo:
last_stat_per_day = Stats.objects.extra(
select={'the_date': 'date(created)' }
).values_list('the_date').annotate(max_date=Max('created'))
Utilizzando extra
non è l'ideale, ma l'SQL prime qui è semplice, e si presta bene per una sostituzione dipendente driver di database. È necessario sostituire solo lo date(created)
. Puoi completarlo in un metodo su un gestore personalizzato, se lo desideri, e hai quindi estratto con successo questo pasticcio in un'unica posizione.
L'altra opzione è quella di aggiungere un modello DateField
al modello e quindi non è necessario utilizzare l'extra. Dovresti semplicemente sostituire la chiamata values_list
con uno values_list('created_date')
, rimuovere completamente lo extra
e chiamarlo un giorno. Il costo è ovvio: è necessario più spazio di archiviazione.Inoltre, non è intuitivo il motivo per cui si dispone di un campo Date
e DateTime
sullo stesso modello. Mantenere i due in sincrono può anche porre problemi.
Giusto per chiarirmi la testa; se vuoi l'ultimo per ogni giorno - nel tuo esempio non vorresti la crescita del 200 e del 222? – Ewan
sì, è vero. L'ho corretto;) – Jannis