在Django ORM中对数据进行透视和复杂注释

17 投票
2 回答
11075 浏览
提问于 2025-04-16 17:24

Django中的ORM(对象关系映射)让我们可以很方便地给查询结果添加一些额外的信息,这些信息是基于相关数据的。不过,我找不到办法来为不同的过滤数据子集添加多个这样的额外信息。

这个问题是关于django-helpdesk的,这是一个开源的、基于Django的故障单追踪系统。我需要将数据整理成这样,以便于制作图表和报告。

考虑这些模型:

CHOICE_LIST = (
    ('open', 'Open'),
    ('closed', 'Closed'),
)

class Queue(models.model):
    name = models.CharField(max_length=40)

class Issue(models.Model):
    subject = models.CharField(max_length=40)
    queue = models.ForeignKey(Queue)
    status = models.CharField(max_length=10, choices=CHOICE_LIST)

还有这个数据集:

队列:

ID | Name
---+------------------------------
1  | Product Information Requests
2  | Service Requests

问题:

ID | Queue | Status
---+-------+---------
1  | 1     | open
2  | 1     | open
3  | 1     | closed
4  | 2     | open
5  | 2     | closed
6  | 2     | closed
7  | 2     | closed

我希望看到的额外信息/汇总结果大概是这样的:

Queue ID | Name                          | open | closed
---------+-------------------------------+------+--------
1        | Product Information Requests  | 2    | 1
2        | Service Requests              | 1    | 3

这基本上就是一个交叉表或数据透视表,用Excel来说就是这个意思。我现在是通过一些自定义的SQL查询来生成这个结果,但如果能用Django的ORM来实现,就能更轻松地动态过滤数据,而不需要在SQL中插入复杂的WHERE条件。

如果想要“额外加分”:如果数据透视的字段(上面例子中的status)是日期,我们想要的列是按月/周/季度/天来划分,应该怎么做呢?

2 个回答

3

Django自从最初提出这个问题以来,增加了很多功能到它的ORM(对象关系映射)中。自从Django 1.8版本开始,如果你想要转换数据,可以使用Case/When这种条件表达式。还有一个第三方应用可以帮你做到这一点,叫做PyPI,你可以在这里找到相关的文档。

5

你有Python,就用它吧。

from collections import defaultdict
summary = defaultdict( int )
for issue in Issues.objects.all():
    summary[issue.queue, issue.status] += 1

现在你的 summary 对象有一个队列和状态,组成了一个二元组的键。你可以直接显示它,使用各种模板技巧。

或者,如果这样更简单的话,你也可以把它重新整理成一个像表格一样的结构。

table = []
queues = list( q for q,_ in summary.keys() )
for q in sorted( queues ):
    table.append( q.id, q.name, summary.count(q,'open'), summary.count(q.'closed') )

你有很多很多的Python技巧可以用来制作数据透视表。

如果你进行测量,你可能会发现,像这样的主要用Python的解决方案实际上比纯SQL的解决方案更快。为什么呢?因为映射操作可能比需要排序的SQL算法在进行分组时要快。

撰写回答