Фильтр Django путем перебора каждого дня диапазона дат
У меня есть модель Orders
, которая предназначена для веб-приложения электронной коммерции, и я хотел бы узнать, сколько заказов было сделано за каждый день в прошлом году. Поэтому я отфильтровал заказы следующим образом:
product_orders = Order.objects.filter(
ordered=True, variant__product=self, date__range=[(timezone.now() - timedelta(weeks=52)), timezone.now()])
Я бы хотел просмотреть каждый день из этого временного диапазона и получить количество заказов за каждый день, результат должен выглядеть примерно так:
[
{
date: "2021-06-21",
quantity: 6
},
{
date: "2021-06-22",
quantity: 0
}
]
где количество составляет Order.objects.filter(...).count()
Вы можете аннотировать свой Order
числом Order
с:
from django.utils.timezone import now
from django.db.models import Count
from django.db.models.functions import TruncDate
now_ = now()
product_orders = Order.objects.filter(
ordered=True,
variant__product=self,
created_at__range=(now_-timedelta(weeks=52)), now_())
).values(
date=TruncDate('created_at__date')
).annotate(
quantity=Count('pk')
).order_by('date')
Это вернет QuerySet
со словарями в качестве элементов, таким образом, они выглядят как:
<QuerySet [
{'date': date(2021, 6, 21), 'quantity': 6 },
{'date': date(2021, 6, 23), 'quantity': 3 }
]>
Если на определенную дату нет заказов, то эта дата не будет включена в вывод. Таким образом, нам придется выполнить постобработку данных.
Мы можем сделать это, сначала работая со словарем, который отображает даты на количества, мы можем сделать это с помощью:
temp = { po['date'].date(): po['quantity'] for po in product_orders }
и затем установите значение по умолчанию 0
для date
и добавьте их в список result
:
result = [
{'date': (now_-timedelta(days=dy)).date(), 'quantity': temp.setdefault((now_-timedelta(days=dy)).date(), 0)}
for dy in range(52*7+1)
]
then result
содержит список словарей с date
и quantity
.
Я просто перебираю даты за последний год, затем фильтрую и подсчитываю
product_orders = Order.objects.filter(
ordered=True,
variant__product=self,
ordered_date__range=(timezone.now()-timedelta(weeks=52), timezone.now()))
result = []
# looping through to get each day from now to a year ago
for day in range(52*7+2):
date = (timezone.now()-timedelta(days=day)).date().isoformat()
result.append({
"day": date,
"quantity": product_orders.filter(ordered_date__date=date).count()
})
это решение дало мне точные результаты, если у вас есть более эффективный способ, я открыт для него. BTW Я изменил created_at
на ordered_date
БОЛЕЕ ЭФФЕКТИВНОЕ РЕШЕНИЕ
now_ = timezone.now
product_orders = Order.objects.filter(
ordered=True,
variant__product=self,
ordered_date__range=((now_()-timedelta(weeks=52)), now_())
).annotate(date=functions.TruncDay("ordered_date")).values("date").annotate(quantity=Count("id")).values("date", "quantity").order_by('date')
temp = {po['date'].date(): po['quantity'] for po in product_orders}
result = [
{'date': (now_()-timedelta(days=dy)).date(),
'quantity': temp.setdefault((now_()-timedelta(days=dy)).date(), 0)}
for dy in range(52*7+1)
]