【问题标题】:Django aggregation on a date range日期范围内的 Django 聚合
【发布时间】:2010-05-23 13:55:16
【问题描述】:

我已经在这里潜伏和学习了一段时间。现在我有一个问题,不知何故我看不到一个简单的解决方案。为了学习 django,我正在构建一个基本上跟踪预订项目的应用程序。 我想做的是显示在选定的年份每个月有多少天预订了一件物品。

我有以下型号:

Asset(Model)

BookedAsset(Model):
 asset = models.ForeignKey(Asset)
 startdate = models.DateField()
 enddate = models.DateField()

所以有以下条目:

asset 1, 2010-02-11, 2010-02-13
asset 2, 2010-03-12, 2010-03-14
asset 1, 2010-04-30, 2010-05-01

我希望得到以下回报:

asset 1    asset 2
-------    -------
Jan = 0    Jan = 0
Feb = 2    Feb = 0
Mar = 0    Mar = 2
Apr = 1    Apr = 0
May = 1    May = 0
Jun = 0    Jun = 0
Jul = 0    Jul = 0
Aug = 0    Aug = 0
Sep = 0    Sep = 0
Oct = 0    Oct = 0
Nov = 0    Nov = 0
Dec = 0    Dec = 0

我知道我需要首先获取日期范围内的天数(并跟踪它们是否超出当前月份并进入下个月),然后对天数进行汇总。我只是坚持如何在 Django 中优雅地做到这一点。

非常感谢任何帮助(或正确方向的提示)。

【问题讨论】:

    标签: python django date django-aggregation


    【解决方案1】:

    我想不出用你的模型结构来做这件事的方法。

    这是一个相当复杂的要求,但无论您如何解决它都可能需要大量自定义 SQL。我认为作为开始,您可能需要考虑更改结构,以便拥有一个 BookedAssetDay 表,该表分别代表预订的每一天。

    class BookedAsset(models.Model):
        asset = models.ForeignKey(Asset)
        day = models.DateField()
    

    然后查询看起来像:

    BookedAsset.objects.extra(
        select={'month': 'MONTH(day)'}
    ).values('asset', 'month').annotate(Count('bookedasset__month'))
    

    【讨论】:

    • 最后,我想我会通过添加一个额外的表格来采用您的解决方案。唯一的问题是,有多个用户拥有许多资产,因此 BookedAssetDay 表可以变得很大......
    • 或者我也可以使用这个答案的解决方案:stackoverflow.com/questions/1371280/… 无论如何,我感谢你的回复,丹尼尔! :)
    【解决方案2】:

    我不会选择上述任何一个答案(我什至喜欢编写一些自定义 SQL,但我至少 5 年没有这样做了)而且你的模型足够简单,可以在关系范式中使用。

    您正在寻找的答案是 django 聚合功能中的 annotate() 函数: http://docs.djangoproject.com/en/dev/topics/db/aggregation/ 您只需要知道如何获取日期的月份,查询集文档中对此进行了说明: http://docs.djangoproject.com/en/dev/ref/models/querysets/#month

    为一项资产获取它的粗略示例:

    BookedAsset.objects.filter(asset=asset).annotate(month_count=Count('startdate__month')).order_by('startdate__month')
    

    (显然是错误的,但我无法重新创建你的结构来给你准确正确的陈述,摆弄一下并阅读文档)

    您甚至可以通过使用同一文档中的连接语法一次性处理所有资产。

    【讨论】:

      【解决方案3】:

      最后,我使用自定义查询来解决我的问题:

      cursor = connection.cursor()
      cursor.execute("""select to_char(allmonths.yeardate::date, 'YYYY/MM/DD') as monthdate,
                          COUNT("day") as bookings
                          from (
                          select distinct date_trunc('month', (date %s - offs)) as yeardate 
                          from generate_series(0,365,28) as offs
                          ) as allmonths
                          left join bookings_bookedassetday 
                          on EXTRACT(MONTH from "day")=EXTRACT(MONTH from yeardate) 
                          and asset_id=%s and EXTRACT(YEAR from "day") = %s
                          group by allmonths.yeardate
                          order by allmonths.yeardate asc""", [year+'-12-31', id, year])
      
      query = cursor.fetchall()
      

      也许这不是最 django-ist 的做法,但我发现它比用纯 django 搞清楚要容易得多:|

      如果其他人有更好的选择,我会全力以赴:)

      【讨论】:

      • 你应该接受这个答案,这样社区就可以一眼看出你不需要更多帮助。
      【解决方案4】:

      还有另一个解决方案是通过数据库视图并使用元类选项 managed = False 将该视图映射到 django 模型

      这里有详细解释:http://anthony-tresontani.github.com/Django/2012/09/12/wka-django-orm-limitations/

      【讨论】:

        猜你喜欢
        • 2014-01-29
        • 2014-06-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多