Курс по Django: https://stepik.org/a/183363
На этом занятии
мы с вами рассмотрим несколько агрегирующих методов. С одним из них, мы в
принципе, уже знакомы – это метод count(), который
подсчитывает число записей. В самом простом случае, с его помощью можно
определить число записей в таблице women:
В результате у
нас формируется SQL-запрос с вызовом функции COUNT() на уровне
самой СУБД:
SELECT
COUNT(*) AS "__count" FROM "women_women"
То есть, число
записей подсчитывается в БД, а нам лишь возвращается вычисленный результат. Это
большой плюс такого подхода: нам нет необходимости передавать большие блоки
данных лишь для того, чтобы, скажем, подсчитать число записей. Все делается «на
месте» в БД и это часто заметно ускоряет подобные процессы. Именно поэтому так
важны агрегирующие функции. Информацию о них можно почитать на странице
документации:
https://docs.djangoproject.com/en/4.2/ref/models/querysets/#aggregation-functions
Подробно о том,
что такое агрегация на уровне SQL-запросов, я уже рассказывал на занятии
по SQLite и, если вы мало
знакомы с этой информацией, то дополнительно советую посмотреть это видео:
https://www.youtube.com/watch?v=KXdiuTOEFGA
Вообще ORM Django поддерживает
следующие основные команды агрегации: Count, Sum, Avg, Max, Min. Для этого их
вначале следует импортировать из ветки django.db.models:
from django.db.models import Count, Sum, Avg, Max, Min
И прописывать в специальном
методе aggregate(). Например, воспользуемся моделью Husband и найдем
минимальный возраст мужчин:
Husband.objects.aggregate(Min("age"))
На выходе
получим словарь:
{'age__min': 30}
где ключ
формируется, как имя поля, два подчеркивания и имя агрегирующей функции, а
далее значение этого поля. В данном случае получили минимальный возраст, равный
30.
Также можно
прописывать сразу несколько агрегирующих функций через запятую, например:
Husband.objects.aggregate(Min("age"), Max("age"))
Получим словарь:
{'age__min':
30, 'age__max': 101}
Здесь уже два
ключа с соответствующими именами. Если по каким-либо причинам стандартные ключи
нам не подходят, и мы бы хотели их поменять, то делается это так:
Husband.objects.aggregate(young=Min("age"), old=Max("age"))
на выходе
получим:
{'young': 30,
'old': 101}
С агрегирующими
значениями можно выполнять различные математические операции, например:
Husband.objects.aggregate(res=Sum("age") - Avg("age"))
причем параметр res в таком случае
прописывать строго обязательно, имя ключа автоматически не генерируется.
По аналогии
используются все остальные агрегирующие операции:
Women.objects.aggregate(Avg("id"))
или так:
Women.objects.filter(pk__gt=2).aggregate(res=Count("cat_id"))
Здесь агрегация
выполняется не для всех записей, а только для тех, у которых id больше 2.
Метод values()
Во всех наших
примерах выше, при выборке записей автоматически возвращались все поля. Если
это была таблица women, то получали девять полей от id до husband_id. Но часто этого
не требуется и достаточно ограничиться несколькими из них. Кроме того, такое
ограничение положительно сказывается на скорости обращения к БД.
Итак, для
указания нужных полей в выборке, используется метод values() с указанием
названий полей, например, так:
Women.objects.values("title", "cat_id").get(pk=1)
На выходе имеем
запись только с двумя полями. Причем, смотрите, если мы укажем взять данные из
связанной таблицы для имени категории:
Women.objects.values("title", "cat__name").get(pk=1)
то ORM Django сформирует
запрос с использованием оператора JOIN SQL-запроса:
SELECT
"women_women"."title",
"women_category"."name" FROM "women_women" INNER
JOIN "women_category" ON ("women_women"."cat_id"
= "women_category"."id") WHERE
"women_women"."id" = 1 LIMIT 21
Благодаря такой
конструкции одним запросом выбираются все нужные данные. Или, даже так:
w = Women.objects.values("title", "cat__name")
При выполнении
этой строчки пока ни один SQL-запрос выполнен не был, т.к. запросы в ORM Django «ленивые»,
обращение к БД происходит только в момент получения данных. Но, если вывести
список постов:
for p in w:
print(p["title"], p["cat__name"])
то увидим, что
для этой операции также был сделан всего один запрос. То есть, ORM Django достаточно
хорошо оптимизирует процесс обращения к БД.
Курс по Django: https://stepik.org/a/183363