Django ORM从数据中选择,合并,提取并排序

时间:2018-09-01 07:46:43

标签: django orm

我在使用django orm执行简单转换时遇到麻烦。 所需的结果应如下所示:

2018-08
2018-07
2018-06
...

并使用此sql创建:

select
    distinct
    strftime('%Y',a."Buchung") || "-" ||
    strftime('%m',a."Buchung") as YearMonth
from
    hhdata_transaktion a
order by
    1 desc

我需要ModelChoiceField作为查询集,所以我在这里绑定到ORM吗?

我的尝试

from django.db.models.functions import TruncMonth, TruncYear

Transaktion.objects
.annotate(year=TruncYear('Buchung'),
          month=TruncMonth('Buchung'))
.distinct()
.order_by('-year', '-month')
.values('year','month')

返回:

<QuerySet [{'year': datetime.date(2018, 1, 1), 'month': datetime.date(2018, 8, 1)}, {'year': datetime.date(2018, 1, 1), 'month': datetime.date(2018, 7, 1)}, {'year': datetime.date(2018, 1, 1), 'month': datetime.date(2018, 6, 1)}, {'year': datetime.date(2018, 1, 1), 'month': datetime.date(2018, 5, 1)}, {'year': datetime.date(2018, 1, 1), 'month': datetime.date(2018, 4, 1)}, {'year': datetime.date(2018, 1, 1), 'month': datetime.date(2018, 3, 1)}, {'year': datetime.date(2018, 1, 1), 'month': datetime.date(2018, 2, 1)}, {'year': datetime.date(2018, 1, 1), 'month': datetime.date(2018, 1, 1)}, {'year': datetime.date(2017, 1, 1), 'month': datetime.date(2017, 12, 1)}, {'year': datetime.date(2017, 1, 1), 'month': datetime.date(2017, 11, 1)}, {'year': datetime.date(2017, 1, 1), 'month': datetime.date(2017, 10, 1)}, {'year': datetime.date(2017, 1, 1), 'month': datetime.date(2017, 9, 1)}, {'year': datetime.date(2017, 1, 1), 'month': datetime.date(2017, 8, 1)}]>

我感觉自己离预期的结果还很遥远。

2 个答案:

答案 0 :(得分:1)

如果要获取年份或月份,可以分别使用ExtractYear [Django-doc]ExtractMonth [Django-doc]。截断将为您提供年或月的开始

因此我们可以将查询重写为:

from django.db.models.functions import ExtractMonth, ExtractYear

qs = Transaktion.objects.annotate(
    year=ExtractYear('Buchung'),
    month=ExtractMonth('Buchung')
).order_by('-year', '-month').values('year','month').distinct()

尽管可以在SQL级别进行处理,但我认为这会使工作更加复杂。例如,如果在SQL中连接数字,则可能需要做一些工作才能在几个月内(小于10)获得前导零。此外,该查询可能包含特定于“ SQL方言”的功能,从而使其便携性较低。

因此,我建议在Django / Python级别进行后期处理。例如:

from django.db.models.functions import ExtractMonth, ExtractYear

class MyForm(forms.Form):
    my_choice_field = forms.ChoiceField()

    # ...

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        qs = Transaktion.objects.annotate(
            year=ExtractYear('Buchung'),
            month=ExtractMonth('Buchung')
        ).order_by('-year', '-month').values('year','month').distinct()
        self.fields['my_choice_field'].choices = [
            (row['year']*100+row['month'], '{}-{:02d}'.format(row['year'], row['month'])
            for row in qs
        ]

因此,我们在此处生成一个由2个元组组成的列表,其中第一个元素是用于标识选择的某种数字(我在这里将年份乘以100,因此201804是2018年4月)。元组的第二个元素是确定格式的字符串。

答案 1 :(得分:0)

如果您想要像2018-06这样的字符串列表,类似的东西应该可以工作:

[ '%i-%02i' % (x.Buchung.year, x.Buchung.month) for x in Transaktion.objects.order_by(-Buchung) ]