Вы находитесь на странице: 1из 23

Введение

Запрос и фильтрация
1. Как найти запрос, связанный с набором запросов?
2. Как делать OR-запросы в Django ORM?
3. Как делать AND-запросы в Django ORM?
4. Как сделать NOT запрос в Django queryset?
5. Как выполнить объединение двух наборов запросов из одной или разных
моделей?
6. Как выбрать только некоторые поля в наборе запросов?
7. Как сделать подзапросное выражение в Django?
8. Как отфильтровать набор запросов с критериями на основе сравнения
значений их полей
9. Как отфильтровать поле FileField без какого-либо файла?
10. Как выполнять операции объединения в django ORM?
11. Как найти вторую по величине запись с помощью Django ORM?
12. Найти строки с дублирующимися значениями полей
13. Как найти значения разных полей из набора запросов?
14. Как использовать объекты Q для сложных запросов?
15. Как группировать записи в Django ORM?
16. Как эффективно выбрать случайный объект из модели?
17. Как использовать произвольные функции базы данных в кверисетах?

Создание, обновление и удаление записей.


1. Как создать несколько объектов в одном запросе?
2. Как скопировать или клонировать существующий объект модели?
3. Как сделать так, чтобы можно было создать только один объект?
4. Как обновить денормализованные поля в других моделях при
сохранении?
5. Как выполнить операцию truncate like с помощью Django ORM?
6. Какие сигналы подает Django во время создания или обновления
объекта?
7. Как преобразовать строку в дату и сохранить в базе данных?
Сортировка в Django ORM¶
1. Как упорядочить набор запросов по возрастанию или убыванию?
2. Как упорядочить набор запросов без учета регистра?
3. Как сделать сортировку по двум полям
4. Как сделать сортировку по полю из связанной модели (с внешним
ключом)?
5. Как сделать сортировку по аннотированному (вычисляемому) полю?
Моделирование баз данных
1. Как моделировать отношения один на один?
2. Как смоделировать отношения «один ко многим»?
3. Как смоделировать отношения «многие ко многим»?
4. Как включить самоссылающийся ключ ForeignKey в модель
5. Как преобразовать существующие базы данных в модели Django?
6. Как добавить модель для представления базы данных?
7. Как создать общую модель, которая может быть связана с любым типом
сущности? (Например, категория или комментарий?).
8. Как указать имя таблицы для модели?
9. Как указать имя столбца для поля модели?
10. В чем разница между null=True и blank=True?
11. Как использовать UUID вместо ID в качестве первичного ключа?
12. Как использовать поле slug в django для большей читабельности?
13. Как добавить несколько баз данных в приложение django?

Тестирование
1. Как убедитьтся, что функция использовала фиксированное количество
запросов?
2. Как ускорить тестирование путем повторного использования базы данных
между прогонами теста?
3. Как перезагрузить объект модели из базы данных?

Индексы и таблицы
Алфавитный указатель
Состав модуля
Поиск
Введение

Django ORM является одним из ключевых столпов Django. Он предоставляет


абстракции для работы с базами данных, в основном не зависящие от баз
данных.

Django ORM сочетает в себе простоту использования с мощными


абстракциями. Он поддерживает принцип «Простые вещи просты, а
сложные вещи возможны».

В этой книге мы будем изучать Django ORM, делая с ним разные вещи. Мы
зададим около 50 вопросов о Django ORM и получим более глубокое
понимание ORM.

Как читать эту книгу.


Каждая глава книги состоит из вопросов. Вопросы сгруппированы в
соответствующих главах. Вы можете читать книгу одним из двух способов.
Если вы хотите получить ответы на конкретные вопросы, прочитайте эту
главу и другие главы в этой группе.

Если вам нужно глубже понять Django ORM и слой моделей, прочитайте эти
главы от начала и до конца.
1. Как найти запрос, связанный с набором запросов?

Иногда вы хотите узнать, как Django ORM заставляет наши запросы


выполняться или какой SQL соответствует коду, который вы пишете. Это
очень просто. Вы можете получить str из любого queryset.query, чтобы
получить sql.

У вас есть модель под названием Event. Для получения всех записей вы
напишете что-то вроде Event.objects.all(), затем сделаете
str(queryset.query).

>>> queryset = Event.objects.all()


>>> str(queryset.query)

SELECT "events_event"."id", "events_event"."epic_id",


"events_event"."details", "events_event"."years_ago" FROM "events_event"

Пример 2

>>> queryset = Event.objects.filter(years_ago__gt=5)


>>> str(queryset.query)
SELECT "events_event"."id", "events_event"."epic_id", "events_event"."details",
"events_event"."years_ago" FROM "events_event"
WHERE "events_event"."years_ago" > 5
2. Как делать OR-запросы в Django ORM?¶

Если вы используете django.contrib.auth, у вас будет таблица под названием


auth_user. В ней будут поля username, first_name, last_name и другие.

Частым требованием является выполнение фильтрации OR с двумя и более


условиями. Скажем, вы хотите найти всех пользователей с именем,
начинающимся на „R“, и фамилией, начинающейся на „D“.

Django предоставляет два варианта.


queryset_1 | queryset_2
filter(Q(<condition_1>)|Q(<condition_2>)

2.1. Запрос в деталях


SQL-запрос для приведенного выше условия будет выглядеть примерно так:

SELECT username, first_name, last_name, email FROM auth_user WHERE


first_name LIKE 'R%' OR last_name LIKE 'D%';
Аналогичным образом наш ORM-запрос будет выглядеть следующим
образом:

Вы также можете посмотреть на сгенерированный запрос.

In [5]: str(queryset.query)Out[5]: 'SELECT "auth_user"."id", "auth_user"."password",


"auth_user"."last_login","auth_user"."is_superuser", "auth_user"."username",
"auth_user"."first_name","auth_user"."last_name", "auth_user"."email",
"auth_user"."is_staff","auth_user"."is_active", "auth_user"."date_joined" FROM "auth_user"
WHERE ("auth_user"."first_name"::text LIKE R% OR "auth_user"."last_name"::text LIKE D%)'

В качестве альтернативы можно использовать объекты Q.

from django.db.models import Q


qs = User.objects.filter(Q(first_name__startswith='R')|Q(last_name__startswith='D'))

Если вы посмотрите на сформированный запрос, результат будет точно


таким же
3. Как делать AND-запросы в Django ORM?

Если вы используете django.contrib.auth, у вас будет таблица под названием


auth_user. В ней будут поля username, first_name, last_name и другие.

Часто возникает необходимость выполнить операцию AND, чтобы найти


наборы запросов, соответствующие нескольким критериям.

Допустим, вы хотите найти пользователей, у которых firstname начинается


с „R“ И last_name начинается с „D“.

Django предоставляет три варианта.


filter(<condition_1>, <condition_2>)
queryset_1 & queryset_2
filter(Q(<condition_1>) & Q(<condition_2>))

3.1. Запрос в деталях


Наш SQL-запрос для приведенного выше условия будет выглядеть примерно
так

SELECT username, first_name, last_name, email FROM auth_user WHERE


first_name LIKE 'R%' AND last_name LIKE 'D%';

Стандартным способом объединения нескольких условий в filter является


AND, поэтому вы можете просто сделать.

queryset_1 =User.objects.filter(first_name__startswith='R',last_name__startswith='D')

В качестве альтернативы вы можете явно использовать оператор & для


наборов запросов.
queryset_2 = User.objects.filter(
first_name__startswith='R') &User.objects.filter(
last_name__startswith='D')

Для полной настраиваемости можно использовать объекты Q.

Вы можете посмотреть на сгенерированный запрос и убедиться, что все они


одинаковы.

In [10]: str(queryset_2.query)
Out[10]: 'SELECT "auth_user"."id", "auth_user"."password", "auth_user"."last_login",
"auth_user"."is_superuser", "auth_user"."username", "auth_user"."first_name",
"auth_user"."last_name", "auth_user"."email", "auth_user"."is_staff",
"auth_user"."is_active", "auth_user"."date_joined" FROM "auth_user" WHERE
("auth_user"."first_name"::text LIKE R% AND "auth_user"."last_name"::text LIKE D%)'

In [11]: str(queryset_1.query) == str(queryset_2.query) == str(queryset_3.query)


Out[11]: True

Стандартным способом объединения нескольких условий в filter является


AND, поэтому вы можете просто сделать.

queryset_1 =User.objects.filter(first_name__startswith='R',last_name__startswith='D')

В качестве альтернативы вы можете явно использовать оператор & для


наборов запросов.
4. Как сделать NOT запрос в Django queryset?

Если вы используете django.contrib.auth, у вас будет таблица под названием


auth_user. В ней будут поля username, first_name, last_name и другие.

Допустим, вы хотите получить всех пользователей с id НЕ < 5. Вам нужна


операция NOT.

Django предоставляет два варианта.


exclude(<condition>)
filter(~Q(<condition>))

4.1. Запрос в деталях


Наш SQL-запрос для приведенного выше условия будет выглядеть примерно
так:

SELECT id, username, first_name, last_name, email FROM auth_user WHERE NOT id < 5;

Метод 1 с использованием исключения


Метод 2 с использованием метода Q():

>>> from django.db.models import Q


>>> queryset = User.objects.filter(~Q(id__lt=5))
>>> queryst<QuerySet [<User: Ritesh>, <User: Billy>, <User: Radha>, <User:
sohan>, <User: Raghu>, <User: rishab>]>
5. Как выполнить объединение двух наборов запросов из одной
или разных моделей?
Оператор UNION используется для объединения наборов результатов двух
или более наборов запросов. Наборы запросов могут быть из одной и той
же или из разных моделей. Если наборы запросов из разных моделей, поля
и их типы данных должны совпадать.

Давайте продолжим нашу модель auth_user и создадим 2 кверисета для


выполнения операции объединения

>>> q1 = User.objects.filter(id__gte=5)
>>> q1<QuerySet [<User: Ritesh>, <User: Billy>, <User: Radha>, <User: sohan>, <User:
Raghu>, <User: rishab>]

>>>> q2 = User.objects.filter(id__lte=9)
>>>> q2<QuerySet [<User: yash>, <User: John>, <User: Ricky>, <User: sharukh>, <User:
Ritesh>, <User: Billy>, <User: Radha>, <User: sohan>, <User: Raghu>]

>>>> q1.union(q2)<QuerySet [<User: yash>, <User: John>, <User: Ricky>, <User:


sharukh>, <User: Ritesh>, <User: Billy>, <User: Radha>, <User: sohan>, <User: Raghu>,
<User: rishab>]

>>>> q2.union(q1)<QuerySet [<User: yash>, <User: John>, <User: Ricky>, <User:


sharukh>, <User: Ritesh>, <User: Billy>, <User: Radha>, <User: sohan>, <User: Raghu>,
<User: rishab>]>

Теперь попробуйте это

>>> q3 = EventVillain.objects.all()
>>> q3<QuerySet [<EventVillain: EventVillain object (1)>]
>>>> q1.union(q3)

django.db.utils.OperationalError: SELECTs to the left and right of UNION do


not have the same number of result columns

Операция объединения может быть выполнена только с наборами


запросов, имеющими одинаковые поля и типы данных. Поэтому при
последней операции объединения возникла ошибка. Вы можете выполнить
объединение двух моделей, если они имеют одинаковые поля или
одинаковые подмножества полей.

Поскольку Hero и Villain оба имеют name и gender, мы можем


использовать values_list для ограничения выбранных полей, а затем
выполнить объединение.
Это даст вам все объекты Hero и << 1 >>> с их именем и полом.
6. Как выбрать только некоторые поля в наборе запросов?

В модели auth_user имеется ряд полей. Но иногда вам не нужно


использовать все поля. В таких ситуациях мы можем запросить только
нужные поля.

Django предоставляет два способа сделать это

Методы values и values_list на queryset.


only_method

Скажем, мы хотим получить first_name и last_name всех пользователей,


чье имя начинается с R. Вы не хотите получать другие поля, чтобы
уменьшить объем работы БД.

>>> queryset = User.objects.filter(


first_name__startswith='R').values('first_name', 'last_name')

>>> queryset<QuerySet [{'first_name': 'Ricky', 'last_name': 'Dayal'},


{'first_name': 'Ritesh', 'last_name': 'Deshmukh'}, {'first_name': 'Radha',
'last_name': 'George'}, {'first_name': 'Raghu', 'last_name': 'Khan'},
{'first_name': 'Rishabh', 'last_name': 'Deol'}]

Вы можете проверить сгенерированный sql, используя str(queryset.query),


что дает.

SELECT "auth_user"."first_name", "auth_user"."last_name"FROM "auth_user"


WHERE "auth_user"."first_name"::text LIKE R%

Результатом будет список словарей.


В качестве альтернативы вы можете сделать
>>> queryset = User.objects.filter(
first_name__startswith='R').only("first_name", "last_name")

str(queryset.query), дает нам

SELECT "auth_user"."id", "auth_user"."first_name", "auth_user"."last_name"


FROM "auth_user" WHERE "auth_user"."first_name"::text LIKE R%

Единственное различие между only и values заключается в том, что only


также забирает id.
7. Как сделать подзапросное выражение в Django?
Django позволяет использовать подзапросы SQL. Давайте начнем с чего-то
простого, У нас есть модель UserParent, которая имеет OnetoOne
отношение с пользователем auth. Мы найдем все UserParent, у которых
есть UserParent.

>>> from django.db.models import Subquery


>>> users = User.objects.all()
>>> UserParent.objects.filter(user_id__in=Subquery(users.values('id')))
<QuerySet [<UserParent: UserParent object (2)>, <UserParent: UserParent
object (5)>, <UserParent: UserParent object (8)>]>

Теперь о чем-то более сложном. Для каждого Category мы хотим найти


наиболее благосклонного Hero.

Модели выглядят примерно так.

Вы можете найти самого доброжелательного Героя следующим образом

hero_qs = Hero.objects.filter(category=OuterRef("pk")).order_by("-benevolence_factor")

Category.objects.all().annotate(most_benevolent_hero=Subquery(hero_qs.values('name')[:1]))

Если вы посмотрите на сгенерированный sql, вы увидите

SELECT "entities_category"."id","entities_category"."name",(SELECT
U0."name"FROM "entities_hero" U0WHERE U0."category_id" =
("entities_category"."id")ORDER BY U0."benevolence_factor" DESCLIMIT 1) AS
"most_benevolent_hero"FROM "entities_category"

Давайте разберем логику кверисета. Первая часть

hero_qs = Hero.objects.filter(category=OuterRef("pk")).order_by("
benevolence_factor")
Мы упорядочиваем объект Hero по benevolence_factor в порядке DESC и
используем category=OuterRef("pk"), чтобы объявить, что мы будем
использовать его в подзапросе.

Затем мы аннотируем most_benevolent_hero =


Subquery(hero_qs.values('name')[:1]), чтобы получить использование
подзапроса с кверисетом Category. Часть hero_qs.values('name')[:1]
подхватывает первое имя из подзапроса.
8. Как отфильтровать набор запросов с критериями на основе
сравнения значений их полей
Django ORM позволяет легко фильтровать на основе фиксированных
значений. Чтобы получить все User объекты с first_name, начиная с 'R', вы
можете сделать User.objects.filter(first_name__startswith='R').

Что если вы хотите сравнить имя_и фамилию? Вы можете использовать


объект F. Сначала создайте несколько пользователей.

In [27]: User.objects.create_user(email="shabda@example.com",
username="shabda", first_name="Shabda", last_name="Raaj")
Out[27]: <User: shabda>

In [28]: User.objects.create_user(email="guido@example.com",
username="Guido", first_name="Guido", last_name="Guido")
Out[28]: <User: Guido>

Теперь вы можете найти пользователей, у которых first_name==last_name.

In [29]: User.objects.filter(last_name=F("first_name"))
Out[29]: <QuerySet [<User: Guido>]>

F также работает с вычисляемым полем с помощью annotate. Что если нам


нужны пользователи, чьи имя и фамилия содержат одну и ту же букву?

Вы можете установить первую букву из строки с помощью


Substr("first_name", 1, 1), что мы и делаем.

In [41]: User.objects.create_user(email="guido@example.com",
username="Tim", first_name="Tim", last_name="Teters")
Out[41]: <User: Tim>
#...
In [46]: User.objects.annotate(first=Substr("first_name", 1, 1),
last=Substr("last_name", 1, 1)).filter(first=F("last"))
Out[46]: <QuerySet [<User: Guido>, <User: Tim>]>

F может также использоваться с __gt, __lt и другими выражениями.


9. Как отфильтровать поле FileField без какого-либо файла?

FileField или ImageField хранит путь к файлу или изображению. На уровне


БД они аналогичны CharField.

Поэтому, чтобы найти поле FileField без какого-либо файла, мы можем


сделать следующий запрос.

no_files_objects = MyModel.objects.filter(Q(file='')|Q(file=None))

10. Как выполнять операции объединения в django ORM?

Оператор SQL Join используется для объединения данных или строк из двух
или более таблиц на основе общего поля между ними. Объединение может
быть выполнено различными способами. Некоторые из них показаны ниже.:

>>> a1 = Article.objects.select_related('reporter') // Using select_related>>>


a1<QuerySet [<Article: International News>, <Article: Local News>, <Article:
Morning news>, <Article: Prime time>, <Article: Test Article>, <Article:
Weather Report>]
>>>> print(a1.query)SELECT "events_article"."id", "events_article"."headline",
"events_article"."pub_date", "events_article"."reporter_id",
"events_article"."slug", "auth_user"."id", "auth_user"."password",
"auth_user"."last_login", "auth_user"."is_superuser",
"auth_user"."username", "auth_user"."first_name", "auth_user"."last_name",
"auth_user"."email", "auth_user"."is_staff", "auth_user"."is_active",
"auth_user"."date_joined" FROM "events_article" INNER JOIN "auth_user" ON
("events_article"."reporter_id" = "auth_user"."id") ORDER BY
"events_article"."headline" ASC
>>> a2 = Article.objects.filter(reporter__username='John')
>>> a2<QuerySet [<Article: International News>, <Article: Local News>,
<Article: Prime time>, <Article: Test Article>, <Article: Weather Report>]
>>>> print(a2.query)SELECT "events_article"."id", "events_article"."headline",
"events_article"."pub_date", "events_article"."reporter_id",
"events_article"."slug" FROM "events_article" INNER JOIN "auth_user" ON
("events_article"."reporter_id" = "auth_user"."id") WHERE
"auth_user"."username" = John ORDER BY "events_article"."headline" ASC
11. Как найти вторую по величине запись с помощью Django ORM?

Вы можете столкнуться с ситуацией, когда вам нужно найти второго


пользователя по возрасту или зарплате.

Хотя ORM дает возможность найти first(), last() элемент из кверисета, но не


n-й элемент. Это можно сделать с помощью оператора slice.

Мы можем найти N-е количество записей из запроса с помощью оператора


slice.

>>> user = User.objects.order_by('-last_login')[1] // Second Highest record


w.r.t 'last_login'
>>> user.first_name'Raghu'
>>> user = User.objects.order_by('-last_login')[2] // Third Highest record w.r.t
'last_login'
>>> user.first_name'Sohan'

User.objects.order_by('-last_login')[2] извлекает нужный объект из базы


данных только с помощью LIMIT ... OFFSET. Если вы посмотрите на
сгенерированный sql, вы увидите что-то вроде этого.

SELECT
"auth_user"."id","auth_user"."password","auth_user"."last_login","auth_user
"."is_superuser","auth_user"."username","auth_user"."first_name","auth_use
r"."last_name","auth_user"."email","auth_user"."is_staff","auth_user"."is_acti
ve","auth_user"."date_joined"FROM "auth_user"ORDER BY
"auth_user"."last_login" DESCLIMIT 1OFFSET 2
12. Найти строки с дублирующимися значениями полей

Скажем, вам нужны все пользователи, чей first_name совпадает с другим


пользователем.

Вы можете найти дубликаты записей, используя приведенную ниже технику.

>>> duplicates = User.objects.values( 'first_name'


).annotate(name_count=Count('first_name')).filter(name_count__gt=1)
>>> duplicates<QuerySet [{'first_name': 'John', 'name_count': 3}]>
>>> duplicates = User.objects.values( 'first_name'
).annotate(name_count=Count('first_name')).filter(name_count__gt=1)
>>> duplicates<QuerySet [{'first_name': 'John', 'name_count': 3}]>

Если вам необходимо заполнить все записи, вы можете сделать следующее

>>> records = User.objects.filter(first_name__in=[item['first_name'] for item


in duplicates])
>>> print([item.id for item in records])[2, 11, 13]
13. Как найти значения разных полей из набора запросов?

Вы хотите найти пользователей, чьи имена не повторялись. Вы можете


сделать это следующим образом

distinct =
User.objects.values('first_name').annotate(name_count=Count('first_name')).
filter(name_count=1)

records = User.objects.filter(first_name__in=[item['first_name'] for item in


distinct])

Это отличается от User.objects.distinct("first_name").all(), который


подтягивает первую запись, когда встречает отчетливую first_name.

14. Как использовать объекты Q для сложных запросов?


14. Как использовать объекты Q для сложных запросов?

В предыдущих главах мы использовали объекты Q для операций OR и AND


и NOT. Объекты Q обеспечивают полный контроль над пунктом where
запроса.

Если вы хотите OR свои условия.

>>> from django.db.models import Q


>>> queryset = User.objects.filter(
Q(first_name__startswith='R') | Q(last_name__startswith='D'))
>>> queryset
<QuerySet [<User: Ricky>, <User: Ritesh>, <User: Radha>, <User: Raghu>,
<User: rishab>]>

Если вы хотите AND свои условия.

>>> queryset = User.objects.filter(


Q(first_name__startswith='R') & Q(last_name__startswith='D'))
>>> queryset
<QuerySet [<User: Ricky>, <User: Ritesh>, <User: rishab>]>

Если вы хотите найти всех пользователей, чей first_name начинается с „R“,


но не хотите, если last_name имеет „Z“

>>> queryset = User.objects.filter(


Q(first_name__startswith='R') & ~Q(last_name__startswith='Z'))

Если вы посмотрите на сформированный запрос, вы увидите

SELECT
"auth_user"."id","auth_user"."password","auth_user"."last_login","auth_user
"."is_superuser","auth_user"."username","auth_user"."first_name","auth_use
r"."last_name","auth_user"."email","auth_user"."is_staff","auth_user"."is_acti
ve","auth_user"."date_joined"FROM "auth_user"WHERE
("auth_user"."first_name"::text LIKE R%AND NOT
("auth_user"."last_name"::text LIKE Z%))

Вы можете комбинировать объекты Q более сложными способами для


создания сложных запросов.
15. Как группировать записи в Django ORM?

Группировка записей в Django ORM может быть выполнена с помощью


функций агрегирования, таких как Max, Min, Avg, Sum. Запросы Django
помогают создавать, извлекать, обновлять и удалять объекты. Но иногда
нам нужно получить агрегированные значения из объектов. Мы можем
получить их на примере, показанном ниже:

>>> from django.db.models import Avg, Max, Min, Sum, Count


>>> User.objects.all().aggregate(Avg('id')){'id__avg': 7.571428571428571}
>>> User.objects.all().aggregate(Max('id')){'id__max': 15}
>>> User.objects.all().aggregate(Min('id')){'id__min': 1}
>>> User.objects.all().aggregate(Sum('id')){'id__sum': 106}

Продолжение тут : https://django.fun/docs/django-orm-


cookbook/ru/2.0/random/

Мне было лень ))

Вам также может понравиться