Аннотировать набор запросов Django с предыдущим воскресеньем на основе поля даты строки - PullRequest
0 голосов
/ 10 мая 2018

У меня есть таблица с набором Orders, которую мои клиенты сделали (купили, скажем так).

Клиенты могут выбрать дату доставки.Это значение хранится в каждом Order в поле с именем Order.delivery_date (не слишком изобретательно)

class Order(BaseModel):
    customer = ForeignKey(Customer, on_delete=CASCADE, related_name='orders')
    delivery_date = DateField(null=True, blank=True)

Я хотел бы аннотировать набор запросов, который выбирает Orders с предыдущим воскресеньем для этогоdelivery_date (в основном для создания еженедельного отчета, "сгруппированного" в неделю)

Я думал "О! Я знаю! Я получу индекс даты на неделе и вычтуdatetime.timedelta с количеством дней в индексе этой недели, и я буду использовать его для получения воскресенья (например, функции Python .weekday ()) ":

from server.models import *
import datetime
from django.db.models import F, DateField, ExpressionWrapper
from django.db.models.functions import ExtractWeekDay

Order.objects.filter(
    delivery_date__isnull=False
).annotate(
    sunday=ExpressionWrapper(
        F('delivery_date') - datetime.timedelta(days=ExtractWeekDay(F('delivery_date')) + 1),
        output_field=DateField()
    )
).last().sunday

Но если я это сделаюто, что я получаю TypeError: unsupported type for timedelta days component: CombinedExpression при попытке "построить": выражение timedelta.

Не использование функции F в Extract также не имеет значения: я получаюта же ошибка, независимо от того, использую ли я Extract(F('delivery_date')) или Extract('delivery_date')

Это Python 3.4, с Django 2.0.3 поверх MySQL 5.7.21

Я знаю, что всегда могу получитьOrder возьмите объект и сделайте это в Python (у меня даже есть небольшая вспомогательная функция, которая сделает это), но было бы неплохо получить объектс этой аннотацией из БД (а также в учебных целях)

Заранее спасибо.

1 Ответ

0 голосов
/ 11 мая 2018

О, я забыл о extra

Похоже, что это должно делать (по крайней мере, для MySQL)

orders_q = Order.objects.filter(
    delivery_date__isnull=False
).extra(
    select={
        'sunday': "DATE_ADD(`delivery_date`, interval(1 - DAYOFWEEK(`delivery_date`)) DAY)"
    },
).order_by('-id')

Вроде работает:

for record in orders_q.values('sunday', 'delivery_date'):
    print("date: {delivery_date}, sunday: {sunday} is_sunday?: {is_sunday}".format(
        is_sunday=record['sunday'].weekday() == 6, **record)
    )

date: 2018-06-04, sunday: 2018-06-03 is_sunday?: True
date: 2018-05-30, sunday: 2018-05-27 is_sunday?: True
date: 2018-05-21, sunday: 2018-05-20 is_sunday?: True
date: 2018-06-04, sunday: 2018-06-03 is_sunday?: True

РЕДАКТИРОВАТЬ : Судя по всему, extra находится на пути к устареванию / неподдержке. По крайней мере, не очень ... эм ... с любовью получен разработчиками Django. Возможно, было бы лучше использовать RawSQL вместо этого. На самом деле у меня возникли проблемы при попытке выполнить дальнейшую фильтрацию в аннотации sunday с использованием extra, которую я не получаю с помощью метода RawSQL.

Кажется, это работает лучше:

orders_q = orders_q.annotate(
    sunday=RawSQL("DATE_ADD(`delivery_date`, interval(1 - DAYOFWEEK(`delivery_date`)) DAY)", ())
)

Что позволяет мне дальше аннотировать ...

orders_q.annotate(sunday_count=Count('sunday'))

Я не уверен почему, но когда я использовал extra, я бы получил Cannot resolve keyword 'sunday' into field

...