【问题标题】:Django filter by looping through each day of a date rangeDjango通过循环遍历日期范围的每一天进行过滤
【发布时间】:2021-11-08 06:40:48
【问题描述】:

我有一个名为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 模型上的日期字段称为 createdAt,它的 default=timezone.now 所以我想要一些汇总或注释过去一年每天创建的订单的内容

订单模型

class Order(models.Model):
    order_id = models.CharField(max_length=100, blank=True, null=True)
    user = models.ForeignKey(
        User, on_delete=models.CASCADE, related_name="orders")
    address = models.ForeignKey(
        Address, on_delete=models.SET_NULL, null=True, blank=True)
    variant = models.ForeignKey(
        "products.Variant", on_delete=models.CASCADE, related_name="orders")
    price = models.FloatField(blank=True, null=True)
    store = models.ForeignKey(
        "stores.Store", on_delete=models.CASCADE, related_name="orders", null=True, blank=True)
    # Cart
    in_cart = models.BooleanField(default=True)
    # Order
    order_group = models.ForeignKey(
        "OrderGroup", on_delete=models.CASCADE, related_name="orders", null=True, blank=True)
    ordered = models.BooleanField(default=False)
    ordered_date = models.DateTimeField(blank=True, null=True)
    cancelled = models.BooleanField(default=False)
    cancelled_date = models.DateTimeField(blank=True, null=True)
    # Return
    order_return_deadline = models.DateTimeField(blank=True, null=True)
    returning = models.BooleanField(default=False)
    returning_date = models.DateTimeField(blank=True, null=True)
    returned = models.BooleanField(default=False)
    returned_date = models.DateTimeField(blank=True, null=True)
    failed_return = models.BooleanField(default=False)
    # Shipping
    shipped = models.BooleanField(default=False)
    shipped_date = models.DateTimeField(blank=True, null=True)
    merchant_error = models.BooleanField(default=False)
    merchant_error_date = models.DateTimeField(blank=True, null=True)
    # Delivery
    delivered = models.BooleanField(default=False)
    delivered_date = models.DateTimeField(blank=True, null=True)
    failed_delivery = models.BooleanField(default=False)
    failed_delivery_date = models.DateTimeField(blank=True, null=True)
    created_at = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return f"{self.user.last_name}'s Order"

其中数量为Order.objects.filter(...).count()

【问题讨论】:

  • 每天这样很容易,但是没有数量的天不会在查询集中,您需要对结果进行后处理才能填写这些项目。
  • 你能分享你Order模型的相关部分吗?
  • 订单模型上的日期字段称为createdAt,其中有default=timezone.now,所以我想要一些汇总或注释过去一年每天创建的订单@WillemVanOnsem
  • edit提问。一个订单是否有数量(并因此总结数量)或计算订单数(这两个相同)
  • 统计订单数

标签: django django-models django-rest-framework


【解决方案1】:

我只是简单地遍历过去一年的日期,然后过滤和计数

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()
            })

这个解决方案给了我准确的结果,如果你有更有效的方法,我愿意接受。顺便说一句,我将 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)
        ]

【讨论】:

  • 但这会产生 365 次查询,因此效率低下。
  • 你能帮我提高效率吗
  • 这就是为什么我们需要.annotate(...) 来进行一个查询,而不是300+。
【解决方案2】:

您可以使用Orders 的数量来注释您的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 }

然后将dates 的默认值设置为0,并将这些添加到result 列表中:

result = [
    {'date': (now_-timedelta(days=dy)).date(), 'quantity': temp.setdefault((now_-timedelta(days=dy)).date(), 0)}
    for dy in range(52*7+1)
]

然后result 包含带有datequantity 的字典列表。

【讨论】:

  • 非常感谢让我试试这个
  • 每个条目都给我数量为零,我猜是来自Count("pk")你为什么使用pk
  • @Damiisdandy:您是否有该产品过去 52 周的订单并且已订购?
  • @Damiisdandy:如果你看list(prduct_orders),你能看到这两个订单吗?
  • 是的,我可以看到这两个订单的数量,但结果中的数量为零
猜你喜欢
  • 1970-01-01
  • 2015-03-05
  • 1970-01-01
  • 2021-12-30
  • 1970-01-01
  • 1970-01-01
  • 2010-12-23
  • 1970-01-01
  • 2016-08-14
相关资源
最近更新 更多