Django + PostgreSQL: Fill missing dates in a range

时间:2015-07-28 17:00:17

标签: django postgresql django-queryset django-orm django-1.8

I have a table with one of the columns as date. It can have multiple entries for each date.

date         .....
-----------  -----
2015-07-20     ..
2015-07-20     ..
2015-07-23     ..
2015-07-24     ..

I would like to get data in the following form using Django ORM with PostgreSQL as database backend:

date         count(date)
-----------  -----------
2015-07-20        2
2015-07-21        0       (missing after aggregation)
2015-07-22        0       (missing after aggregation)
2015-07-23        1
2015-07-24        1

Corresponding PostgreSQL Query:

WITH RECURSIVE date_view(start_date, end_date) 
AS ( VALUES ('2015-07-20'::date, '2015-07-24'::date) 
     UNION ALL SELECT start_date::date + 1, end_date 
     FROM date_view 
     WHERE start_date < end_date ) 
SELECT start_date, count(date) 
FROM date_view LEFT JOIN my_table ON date=start_date 
GROUP BY date, start_date 
ORDER BY start_date ASC;

I'm having trouble translating this raw query to Django ORM query.

It would be great if someone can give a sample ORM query with/without a workaround for Common Table Expressions using PostgreSQL as database backend.

The simple reason is quoted here:

My preference is to do as much data processing in the database, short of really involved presentation stuff. I don't envy doing this in application code, just as long as it's one trip to the database

As per this answer django doesn't support CTE's natively, but the answer seems quite outdated.

References:

Thanks

2 个答案:

答案 0 :(得分:1)

我认为你不能用纯Django ORM做到这一点,我甚至不确定这是否可以用extra()整齐地完成。 Django ORM在处理通常的东西方面非常出色,但对于更复杂的SQL语句和要求,对于DBMS特定的实现更是如此,它还没有完全实现。您可能需要降低到executing raw SQL directly,或者卸载应用程序层要执行的任务。

您总是可以使用Python生成缺少的日期,但如果元素的范围和数量很大,那将会非常慢。如果这是AJAX请求其他用途(例如图表),那么你可以将其卸载到Javascript。

答案 1 :(得分:-2)

您可以使用generate_series()来构建日历表,而不是递归CTE:

SELECT calendar, count(mt.zdate) as THE_COUNT
FROM generate_series('2015-07-20'::date
                   , '2015-07-24'::date
                   , '1 day'::interval)  calendar
LEFT JOIN my_table mt ON mt.zdate = calendar
GROUP BY 1
ORDER BY 1 ASC;

BTW:我将date重命名为zdate。 DATE是列的错误名称(它是数据类型的名称)