Django ORM – Примеры с несколькими таблицами (агрегация и групповые запросы)

Агрегатные запросы (aggregate)

Функции агрегатных запросов выполняют вычисления над набором значений и возвращают одно значение.

Перед использованием агрегатных запросов в Django необходимо сначала импортировать Avg, Max, Min, Count, Sum (с заглавной буквы) из django.db.models.

from django.db.models import Avg,Max,Min,Count,Sum  #   引入函数

Возвращаемое значение агрегатного запроса имеет тип словаря.

Функция aggregate() является терминальной клаузой для QuerySet, формирует сводное значение, эквивалентна count().

После использования aggregate() тип данных становится словарём, и вы больше не можете использовать некоторые API типа данных QuerySet.

Тип данных даты (DateField) может использовать Max и Min.

В возвращаемом словаре: имя ключа по умолчанию — (имя атрибута плюс __ и имя агрегатной функции), а значение — вычисленное агрегированное значение.

Если вы хотите настроить имя ключа в возвращаемом словаре, можно задать псевдоним:

aggregate(别名 = 聚合函数名("属性名称"))

Вычислите среднюю цену всех книг:

Пример

from django.db.models import Avg,Max,Min,Count,Sum  # Импорт функции
...
res = models.Book.objects.aggregate(Avg("price"))
print(res, type(res))
...

Вычислите общее количество всех книг, самую высокую цену и самую низкую цену:

Пример

res=models.Book.objects.aggregate(c=Count("id"),max=Max("price"),min=Min("price"))
print(res,type(res)


Групповые запросы (annotate)

Групповые запросы обычно используют агрегатные функции, поэтому перед их использованием нужно импортировать Avg, Max, Min, Count, Sum (с заглавной буквы) из django.db.models.

from django.db.models import Avg,Max,Min,Count,Sum  #   引入函数

Возвращаемое значение:

  • После группировки, если использовать values для получения значений, возвращаемое значение — это тип данных QuerySet, содержащий словари.
  • После группировки, если использовать values_list для получения значений, возвращаемое значение — это тип данных QuerySet, содержащий кортежи.

LIMIT в MySQL эквивалентен срезу типа данных QuerySet в ORM.

Примечание:

Поместите агрегатные функции внутрь annotate.

  • Поместите values или values_list перед annotate:values или values_list определяет, по каким полям группировать, а annotate выполняет группировку.

  • values или values_list размещены после annotate:annotate означает группировку непосредственно по pk текущей таблицы; values или values_list указывает, какие поля запрашивать, и вам нужно задать псевдонимы для агрегатных функций в annotate, затем указать эти псевдонимы в values или values_list.

Подготовьте данные и создайте модели

models.py

class Emp(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    salary = models.DecimalField(max_digits=8, decimal_places=2)
    dep = models.CharField(max_length=32)
    province = models.CharField(max_length=32)

class Emps(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    salary =     models.DecimalField(max_digits=8, decimal_places=2)
    dep = models.ForeignKey("Dep", on_delete=models.CASCADE)
    province = models.CharField(max_length=32)
class Dep(models.Model):
    title = models.CharField(max_length=32)
 

Данные:

Выполните в командной строке MySQL:

INSERT INTO `app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('1', 'Линху Чун', '24', '6000.00', 'Отдел продаж', 'Хэнань'); INSERT INTO `app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('2', 'Жэнь Инъин', '18', '8000.00', 'Отдел Гуань Гуна', 'Гуандун'); INSERT INTO `app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('3', 'Жэнь Восин', '56', '10000.00', 'Отдел продаж', 'Гуандун'); INSERT INTO `app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('4', 'Юэ Линшань', '19', '6000.00', 'Отдел Гуань Гуна', 'Хэнань'); INSERT INTO `app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('5', 'Сяолуннюй', '20', '8000.00', 'Отдел Гуань Гуна', 'Хэбэй'); INSERT INTO `app01_dep` (`id`, `title`) VALUES ('1', 'Отдел продаж'); INSERT INTO `app01_dep` (`id`, `title`) VALUES ('2', 'Отдел Гуань Гуна'); INSERT INTO `app01_emps` (`id`, `name`, `age`, `salary`, `province`, `dep_id`) VALUES ('2', 'Линху Чун', '24', '8000.00', 'Хэнань', '1'); INSERT INTO `app01_emps` (`id`, `name`, `age`, `salary`, `province`, `dep_id`) VALUES ('3', 'Жэнь Инъин', '18', '9000.00', 'Гуандун', '2'); INSERT INTO `app01_emps` (`id`, `name`, `age`, `salary`, `province`, `dep_id`) VALUES ('4', 'Жэнь Восин', '57', '10000.00', 'Гуандун', '1'); INSERT INTO `app01_emps` (`id`, `name`, `age`, `salary`, `province`, `dep_id`) VALUES ('5', 'Юэ Линшань', '19', '6000.00', 'Хэнань', '2'); INSERT INTO `app01_emps` (`id`, `name`, `age`, `salary`, `province`, `dep_id`) VALUES ('6', 'Сяолуннюй', '20', '8000.00', 'Хэбэй', '2');

Вычислите самую низкую цену книги для каждого издателя:

Пример

res = models.Publish.objects.values("name").annotate(in_price = Min("book__price"))
print(res)

Следующий вывод можно увидеть в командной строке:

<QuerySet [{'name': 'Example出版社', 'in_price': Decimal('100.00')}, {'name': '明教出版社', 'in_price': Decimal('300.00')}]>

Подсчитайте количество авторов для каждой книги:

Пример

res = models.Book.objects.annotate(c = Count("authors__name")).values("title","c")
print(res)
Вы можете увидеть следующий вывод в командной строке:
<QuerySet [{'title': 'Example', 'c': 1}, {'title': '吸星大法', 'c': 1}, {'title': '冲灵剑法', 'c': 1}]>

Подсчитайте количество авторов для каждой книги, название которой начинается с "dish":

Пример

res = models.Book.objects.filter(title__startswith="dish").annotate(c = Count("authors__name")).values("title","c")
print(res)

Подсчитайте названия книг с более чем одним автором:

Пример

res = models.Book.objects.annotate(c = Count("authors__name")).filter(c__gt=0).values("title","c")
print(res)
Вы можете увидеть следующий вывод в командной строке:
<QuerySet [{'title': 'Example', 'c': 1}, {'title': '吸星大法', 'c': 1}, {'title': '冲灵剑法', 'c': 1}]>

Отсортируйте QuerySet по убыванию в соответствии с количеством авторов каждой книги:

Пример

res = models.Book.objects.annotate(c = Count("authors__name")).order_by("-c").values("title","c")
print(res)

Запросите общую стоимость книг, опубликованных каждым автором:

Пример

res = models.Author.objects.annotate(all = Sum("book__price")).values("name","all")
print(res)

Запрос F()

Экземпляр F() может ссылаться на поля в запросе, чтобы сравнивать значения двух разных полей в одном и том же экземпляре модели.

Созданные ранее фильтры сравнивают значения полей только с константой. Если вы хотите сравнить значения двух полей, нужно использовать F().

Перед использованием необходимо сначала импортировать F из django.db.models.

from django.db.models import F

Использование:

F("字段名称")

F динамически получает значение поля объекта и может выполнять операции.

Django поддерживает операции сложения, вычитания, умножения, деления и взятия по модулю между объектами F() и между объектами F() и константами.

В операции обновления также можно использовать функцию F().

Запросить людей, у которых зарплата больше их возраста:

Пример

from django.db.models import F
...
book=models.Emp.objects.filter(salary__gt=F("age")).values("name","age")
...

Увеличить цену каждой книги на 100 yuan:

Пример

res = models.Book.objects.update(price=F("price")+100)
print(res)

Q() запрос

Перед использованием сначала импортируйте Q из django.db.models:
from django.db.models import Q

Использование:

Q(条件判断)

Например:

Q(title__startswith="菜")

Множественные условия в ранее построенных фильтрах все связаны с помощью AND. Если вам нужно выполнять более сложные запросы (например, операторы OR), вы можете использовать Q.

Объекты Q можно комбинировать с помощью операторов &, |, ~ (AND, OR, NOT).

Приоритет от высокого к низкому: ~, &, |.

Можно смешивать объекты Q и именованные аргументы. Объекты Q и именованные аргументы объединяются с помощью "and" (т.е. запятые рассматриваются как AND), но объекты Q должны быть размещены перед всеми именованными аргументами.

Запросить названия и цены книг, цена которых больше 350 или название которых начинается с "dish".

from django.db.models import Q

Пример

...
res=models.Book.objects.filter(Q(price__gt=350)|Q(title__startswith="dish")).values("title","price")
print(res)
...

Запросить книги, название которых заканчивается на "dish" или которые не выпущены в октябре 2010 года:

Пример

res = models.Book.objects.filter(Q(title__endswith="dish") | ~Q(Q(pub_date__year=2010) & Q(pub_date__month=10)))
print(res)  

Запросить книги с датой публикации в 2004 или 1999 году и названием, содержащим "dish".

Смешивайте объекты Q и именованные аргументы; объекты Q должны идти перед всеми именованными аргументами:

Пример

res = models.Book.objects.filter(Q(pub_date__year=2004) | Q(pub_date__year=1999), title__contains="dish")
print(res)  

Другие расширения