首页 > 解决方案 > Django - 使用查询集左加入日期数组

问题描述

在我的 Django 项目中,我有以下模型:

class Examine(models.Model):
         name = models.CharField(max_length=45)
         date = models.DateField(blank=True, null=True)

我的目标是提出一个查询集,使我能够呈现带有日期列表和日期检查的模板 - 如果有的话。例如,我在数据库中有三个检查:

Math - 01 January 2019, 
History - 03 January 2019
Geoghraphy - 03 January 2019

我对 2019 年 1 月 1 日至 1 月 4 日的日期间隔感兴趣。输出应该是:

January 1
     Math
January 2
     No exams
January 3
     History
     Geography
January 4
     No exams

我有两年使用 Django ORM 的经验,但我什至无法提交我如何尝试实现它的任何尝试。我没有想法。在 Django 中可能吗?

标签: djangodjango-orm

解决方案


从您知道的可能开始 - 使用此处文档中描述的日期范围过滤器从数据库中获取考试。

from datetime import date

start_date = date(2019, 1, 1)
end_date = date(2019, 1, 4)
exams = Examine.objects.filter(date__gte=start_date, date__lte=end_date).order_by('date')

我想你可能会陷入对日期进行“左连接”的想法。这在原始 SQL 查询中是可能的,但在这里不是必需的。

本质上,我们要做的是在从相关日期范围获取考试后,仅在 Python 代码中执行左连接操作。

让我们创建一个可以支持我们想要做的事情的数据结构:

from collections import defaultdict

exams_by_day = defaultdict(list)
for exam in exams:
    exams_by_day[exam.date].append(exam)

因为它是一个defaultdict,如果我们访问一个不存在的键,我们将得到一个空列表。

最后一部分是为左列生成日期列表:

from datetime import timedelta

date_diff = end - start  # a timedelta object (3 days)
dates = []
for i in range(date_diff.days + 1):
    dates.append(start + timedelta(days=i))

最后,我们可以将这两个数据结构都传递到我们的模板中并对其进行迭代以生成您想要的输出:

<dl>
{% for date_ in dates %}
    <dt>{{ date_|date:"F j"}}</dt>
    <dd>
        <ol>
        {% with exams=exams_by_date.date %}
        {% for exam in exams %}
            <li>{{ exam.name }}</li>
        {% empty %}
            <li>No exams</li>
        {% endfor %}
        </ol>
    </dd>
{% endfor %}
</dl>

请参阅https://docs.djangoproject.com/en/2.0/ref/templates/builtins/
以了解with|date:"F j"以上的详细信息。


推荐阅读