Niestety nie ma mowy (że jestem świadoma .. Spojrzałem dość trudne), aby uniknąć korzystania niektóre rodzaj surowego sql, aby osiągnąć to, co chcesz zrobić, (z bieżącym modelem, patrz sam koniec dla innej sugestii). Ale możesz zminimalizować wpływ, pisząc tak mało surowego sql, jak to możliwe. W praktyce witryny django nie muszą być przenośne w różnych bazach danych. Jeśli nie planujesz używać tej aplikacji w innym miejscu lub nie publikujesz jej publicznie, powinieneś być w porządku.
Poniższy przykład dotyczy sqlite. Możesz może zachować mapowanie typów baz danych na funkcje date
, sprawdzić typ sterownika i zastąpić funkcję prawidłową, jeśli jest to konieczne.
>>> 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
pisałem tu wcześniej, że było to tylko jedno zapytanie ale skłamałam - w values_list musi zostać przekształcone tylko zwrócić max_date dla kolejnych zapytań, co oznacza, że działa oświadczenie. To tylko 2 zapytania, które byłyby znacznie lepsze niż funkcja N + 1.
Non-przenośny nieco to:
last_stat_per_day = Stats.objects.extra(
select={'the_date': 'date(created)' }
).values_list('the_date').annotate(max_date=Max('created'))
Korzystanie extra
nie jest idealna, ale surowego sql tutaj jest prosta i nadaje się dobrze do zależnego wymianie sterownika bazy danych. Tylko date(created)
wymaga wymiany. Jeśli chcesz, możesz zawrzeć to w metodzie na menedżerze niestandardowym, a następnie z powodzeniem wyodrębnić ten bałagan w jednym miejscu.
Inną opcją jest po prostu dodanie modelu DateField
, a następnie nie trzeba w ogóle używać więcej. Po prostu zamienisz wywołanie values_list
na values_list('created_date')
, całkowicie usuniesz extra
i nazwij to dziennie. Koszt jest oczywisty - potrzeba więcej miejsca.Jest również nieintuicyjne pytanie o to, dlaczego masz pole Date
i DateTime
w tym samym modelu. Utrzymywanie synchronizacji dwóch osób może również stwarzać problemy.
Tylko po to, aby to wyjaśnić w mojej głowie; jeśli chcesz najnowszego na każdy dzień - w twoim przykładzie nie chcesz wzrostu 200 i 222? – Ewan
Tak, zgadza się. Poprawiłem to;) – Jannis