首页 > 解决方案 > Django:填补按月分组的视图中的空白

问题描述

我的问题似乎很标准,我找到了很多解决方案,但没有一个适用于 Django 视图。

我有带时间戳的项目(“转换”),我想显示每月的项目数量。我的观点是这样的:

2020-05     3
2020-03     2

我需要的是这个:

2020-05     3
2020-04     0
2020-03     2

模型.py:

class Conversion(models.Model):
    timestamp = models.DateTimeField()

views.py (感谢这个帮助):

from django.db.models import Count
from django.db.models.functions import TruncMonth

def conversions_monthly(request):

    conv = Conversion.objects.values(month=TruncMonth(
        'timestamp')).annotate(count=Count('pk')).order_by('-month')
    context = {'conversion': conv}
    return render(request, 'dummy/conversions_monthly.html', context)

模板conversions_monthly.html:

{% extends "dummy/base.html" %}
{% block content %}
<table>
    {% for c in conversion %}
    <tr>
        <td>{{ c.month |date:"Y-m" }}</td>
        <td class="text-right">{{ c.count }}</td>
    </tr>
    {% endfor %}
</table>
{% endblock content %}

数据:

[
{
   "model": "dummy.conversion",
   "pk": 1,
   "fields": {
      "project": 1,
      "timestamp": "2020-03-10T05:00:00Z"
   }
},
{
   "model": "dummy.conversion",
   "pk": 2,
   "fields": {
      "project": 1,
      "timestamp": "2020-03-12T17:00:00Z"
   }
},
{
   "model": "dummy.conversion",
   "pk": 3,
   "fields": {
      "project": 1,
      "timestamp": "2020-05-19T12:00:00Z"
   }
},
{
   "model": "dummy.conversion",
   "pk": 4,
   "fields": {
      "project": 2,
      "timestamp": "2020-05-20T16:10:03Z"
   }
},
{
   "model": "dummy.conversion",
   "pk": 5,
   "fields": {
      "project": 1,
      "timestamp": "2020-05-20T16:30:00Z"
   }
}
]

我想,我必须以某种方式“聚合”最小和最大日期,并且需要类似的东西:

import datetime

from dateutil.relativedelta import relativedelta

result = []

max = date(2020, 5, 20)
min = date(2020, 3, 1)

current = min

while current <= max:
    result.append(current)
    current += relativedelta(months=1)

提供[datetime.date(2020, 3, 1), datetime.date(2020, 4, 1), datetime.date(2020, 5, 1)]

我不知道如何将这些部分组合在一起——或者在 Django 中有完全不同的方法吗?

标签: djangodjango-views

解决方案


您已经走在正确的道路上,这是您拼接在一起的代码

# views.py

def index(request):
    result = []
    # `min` and `max` are keywords in python, so it is bad practice to use them as variable names
    _max = datetime.date(2020, 5, 20)
    _min = datetime.date(2020, 3, 1)
    current = _min
    while current <= _max:
        result.append({"date": current, "count": 0})
        current += relativedelta(months=1)


    conversions = Conversion.objects.values(month=TruncMonth(
        'timestamp')).annotate(count=Count('pk')).order_by('-month')

    # the conditions for checking `i` being smaller than `length` protect you in case there are no conversions in your range
    i = 0
    length = len(result)
    for conversion in conversions:
        while i < length and \
            not (
                result[i]["date"].year == conversion["month"].year and 
                result[i]["date"].month == conversion["month"].month
                ):
            i += 1
        if i < length:
            result[i]["count"] = conversion["count"]

    context = {'conversion': result}
    return render(request, 'dummy/conversions_monthly.html', context)

模板

{% extends "dummy/base.html" %}
{% block content %}
<table>
    {% for c in conversion %}
    <tr>
        <td>{{ c.date |date:"Y-m" }}</td>
        <td class="text-right">{{ c.count }}</td>
    </tr>
    {% endfor %}
</table>
{% endblock content %}

推荐阅读