#python #sql #django #django-orm
#python #sql #django #django-orm
Вопрос:
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
в приведенном выше примере) была дата, а мы хотели, чтобы столбцы были месяцами / неделями / кварталами / днями?
Ответ №1:
У вас есть 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, которые требуют сортировки как части ГРУППИРОВАНИЯ.
Комментарии:
1. Это решение рухнет, если таблица проблем будет большой и ее вряд ли можно рассматривать как общую.
2. Для всех, кто читает это несколько лет спустя: я получаю
collections.defaultdict' object has no attribute 'count'
с python3, также есть опечатка вsummary.count(q.'closed')
Ответ №2:
Django добавил много функциональности в ORM с тех пор, как этот вопрос был задан изначально. Ответ на вопрос о том, как сводить данные начиная с Django 1.8, заключается в использовании условных выражений Case / When. И есть стороннее приложение, которое сделает это за вас, PyPI и документация
Комментарии:
1. django-sql-explorer также создает сводные таблицы.
2. django-sql-explorer — хороший инструмент, и функциональность сводной таблицы может быть полезна в этом случае, но первоначальный вопрос был о том, как создать сводную таблицу в ORM, а django-sql-explorer делает это в javascript после того, как ORM возвращает все необработанные данные.