Поворотные данные и сложные аннотации в Django ORM - PullRequest
13 голосов
/ 11 мая 2011

ORM в Django позволяет нам легко аннотировать (добавлять поля) наборы запросов на основе связанных данных, однако я не могу найти способ получить несколько аннотаций для различных отфильтрованных подмножеств связанных данных.

Это спрашивается в связи с 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, я смогу легче фильтровать данные динамически, не делая изворотливую вставку предложений WHERE в мой SQL.

Для «бонусных баллов»: Как можно было бы сделать это, если в качестве основного поля (status в приведенном выше примере) была дата, и мы хотели, чтобы столбцы были месяцами / неделями / кварталами / днями?

Ответы [ 2 ]

5 голосов
/ 11 мая 2011

У вас есть 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, которые требуют сортировки как часть GROUP-BY.

3 голосов
/ 05 ноября 2017

Django добавил много функциональных возможностей в ORM с тех пор, как этот вопрос был задан изначально. Ответом на то, как сводить данные начиная с Django 1.8, является использование условных выражений Case / When . И есть стороннее приложение, которое сделает это за вас, PyPI и документация

...