【问题标题】:How to annotate count filtered on datetime?如何注释按日期时间过滤的计数?
【发布时间】:2016-07-14 13:38:57
【问题描述】:

我有类别和项目。这些项目有一个结束字段(日期时间)。现在我需要列出所有类别并显示相关项目数和未来项目的项目数。举个例子:

  • Cat Foo,2 件,未来 1 件。
  • 类别 n,未来 n 项。

列表会很大。所以数据库必须做繁重的工作并注释item_countfuture_item_count

型号:

from django.db import models

class Cat(models.Model):
    title = models.CharField(max_length=200)

class Item(models.Model):
    cat = models.ForeignKey(Cat)
    title = models.CharField(max_length=200)
    end = models.DateTimeField()

创建一个类别和两个相关项目。一个过去,一个未来:

from datetime import timedelta
from django.utils import timezone

cat = Cat(title='Cat 1')
cat.save()

item_1 = Item(cat=cat, title="Item 1", end=timezone.now() - timedelta(days=1))
item_1.save()

item_2 = Item(cat=cat, title="Item 2", end=timezone.now() + timedelta(days=1))
item_2.save()

当我注释 item_count 时,它按预期工作:

from django.db.models import Count

Cat.objects.all().annotate(
    item_count=Count('item')).values('title', 'item_count')
# [{'item_count': 2, 'title': u'Cat 1'}]

我不能注释 Item.end (datetime) 过滤。这完全可以通过 Django 查询实现吗?

Cat.objects.all().annotate(
    item_count=Count('item'),
    future_item_count=Count('item').filter(
        end__gt=timezone.now())
    ).values(
        'title', 
        'item_count', 
        'future_item_count'
    )
# AttributeError: 'Count' object has no attribute 'filter'

我希望得到:[{'item_count': 2, 'future_item_count': 1, 'title': u'Cat 1'}]

我也尝试过 RawSQL,但缺乏 SQL 技能:

from django.db.models.expressions import RawSQL

Cat.objects.all().annotate(
    item_count=Count('item'), 
    future_item_count=RawSQL(
        """SELECT COUNT(*) 
           FROM project_item 
           JOIN project_item 
           AS foo 
           ON foo.cat_id = project_cat.id 
           WHERE project_item.end < NOW()""",
        ""
    )).values(
        'title', 
        'item_count', 
        'future_item_count'
    )
# [{'item_count': 2, 'future_item_count': 2L, 'title': u'Cat 1'}]

但是当我将WHERE project_item.end &lt; NOW()" 更改为WHERE project_item.end &gt; NOW()" 时,我得到了相同的结果:

[{'item_count': 2, 'future_item_count': 2L, 'title': u'Cat 1'}]

如何格式化原始 SQL?或者这可以通过 Django 查询来完成吗?

【问题讨论】:

  • 您无法过滤Count。您正在寻找Case 表达式,请查看documentation
  • @dnaranjo 感谢您的建议。我确实阅读了这部分文档,但放弃了该选项,因为它在注释中使用了 then 的值。也许我错过了一些东西。虽然我接受了原始答案,但我仍然想知道如何做到这一点,而不会退回到原始 sql。

标签: django django-annotate


【解决方案1】:

我个人没有使用过RawSQL(仍然使用.extra),但我认为您不需要在RawSQL 声明中使用JOIN project_item。试试看:

RawSQL("""SELECT COUNT(*) 
            FROM project_item 
            WHERE 
                project_item.cat_id = project_cat.id 
                AND project_item.end < NOW()
""")

还有一件事我认为你不应该使用.values AFTER .annotate,而是BEFORE注释。所以你的完整 QuerySet 应该是这样的:

Cat.objects.values('title')\
    .annotate(
        item_count=Count('item'),
        future_item_count=RawSQL("""
            SELECT COUNT(*) 
            FROM project_item 
            WHERE 
                project_item.cat_id = project_cat.id
                AND project_item.end < NOW()
            """)
    )

【讨论】:

  • 谢谢。我会试一试。我只为示例代码添加了值。它打印出更好的结果。在应用程序中,我将使用完整的对象。
  • 我工作!伟大的。 RawSQL 需要两个值。一个是查询,第二个是参数。我给了一个空字符串。
猜你喜欢
  • 2012-03-18
  • 2022-07-16
  • 2013-10-20
  • 1970-01-01
  • 2021-10-09
  • 1970-01-01
  • 1970-01-01
  • 2018-06-17
  • 2021-09-01
相关资源
最近更新 更多