计算django的选择

时间:2015-12-28 16:01:28

标签: python django django-queryset

我有一个类似的模型:

class Questionnaire(models.Model):
    YES_NO_CHOICES = (
        (True, 'Yes'),
        (False, 'No'),
    )
    satisfaction = models.BooleanField(choices=YES_NO_CHOICES, default=True)
    register = models.DateField(auto_now_add=True)

我需要从问卷中得到答案,按月分组并计算“是”和“否”答案。

例如,我有这样的回答:

{
    '2015-11-29': {True: 1, False: 2},
    '2015-11-30': {True: 3, False: 1},
    '2015-12-01': {True: 5, False: 2},
    '2015-12-05': {True: 3, False: 6}
}

我需要一个django查询集来执行以下操作:

{
    {'2015-11-01': {True: 4, False: 3},
    {'2015-12-01': {True: 8, False: 8}
}

日期并不重要,在模板中我只会使用月份值(01,02,03,...,11,12)。

我正在寻找一种pythonic方法,最好使用django中的queryset,而不是字典。

3 个答案:

答案 0 :(得分:3)

首先,我们需要提取月份和年份值以供我们的查询使用。我们使用extra()来完成此操作。不幸的是,Django没有内置支持。

然后,我们可以使用values()按年份和月份进行分组。

最后,我们可以使用annotate()和条件表达式汇总是/否答案:

from django.db import connections
from django.db.models import Case, IntegerField, Sum, When

conn = connections[Questionnaire.objects.db]

Questionnaire.objects.extra(
    select={
        'year': conn.ops.date_trunc_sql('year', 'register'),
        'month': conn.ops.date_trunc_sql('month', 'register'),
    }).values(
        'year', 'month'
    ).annotate(
        yes_count=Sum(
            Case(When(satisfaction=True, then=1),
                 output_field=IntegerField()),
        ),
        no_count=Sum(
            Case(When(satisfaction=False, then=1),
                 output_field=IntegerField()),
        )
    )

您可能还想order_by('year', 'month'),但这不是代码工作所必需的。

结果将是这样的词典列表:

[{'year': '2015-01-01', 'month': '2015-11-01', 'yes_count': 201, 'no_count': 422},
 {'year': '2015-01-01', 'month': '2015-12-01', 'yes_count': 324, 'no_count': 223},
 ...]

如您所见,yearmonth不是数字,而是字符串。但是,您可以通过拆分轻松提取年份和月份:

year = int(item['year'].split('-')[0])
month = int(item['month'].split('-')[1])

答案 1 :(得分:0)

将选择更改为

result = Questionnaire.objects.extra(
     select={'year': "EXTRACT(year FROM register)",
             'month': "EXTRACT(month FROM register)",
             'yes': "satisfaction = 1",
             'no': "satisfaction = 0",
            }).annotate(yes_count=models.Sum("yes"),
                        no_count=models.Sum("no")).order_by("year", "month").values("yes_count", "no_count", "month", "year")
for row in result:
    print("yes {yes_count}, no {no_count}".format(**row))
    print("date {year}-{month}-01".format(**row))

然后

Repository myr = new Repository(datasource);  // let constructor create connection
myr.setAutoCommit(false); 
myr.DAOObject1(parms);   // method wrapper
myr.DAOObject2(parms);   // method wrapper

myr.commitwork();   // method in Repository that calles endAndCommitTransactionLogic 

编辑,我看到你必须使用额外的,所以我不妨包括是和没有列,并让代码看起来更清洁

答案 2 :(得分:0)

对于那些仍在寻找更好方法的人:

from django.db.models import Q
from django.db.models.functions import TruncMonth
Questionnaire.object.annotate(month=TruncMonth('register'))values(month).annotate(
satisfaction_true=Count('id',filter=Q(satisfaction=True),satisfaction_false=Count('id',filter=Q(satisfaction=False))