Django ORM, методы которые не возвращают QuerySet
Методы, выполняющие запрос сразу
В прошлом уроке методы возвращали новый QuerySet и ничего не делали с базой. Методы из этого урока сразу выполняют запрос и возвращают конкретный результат: объект, число, словарь, логическое значение. После них цепочку продолжить нельзя.
Получение одного объекта
Book.objects.get(pk=1) # ровно один или исключение
Book.objects.first() # первый (по сортировке) или None
Book.objects.last() # последний или None
Post.objects.latest("publish") # самый поздний по полю
Post.objects.earliest("publish") # самый ранний
get() выбрасывает DoesNotExist или MultipleObjectsReturned. first()/last() возвращают None — удобно, когда объекта может не быть:
book = Book.objects.filter(title__icontains="война").first()
if book:
...
Создание и обновление
Book.objects.create(title="Нос", author=gogol)
get_or_create
Найти, а если нет — создать. Возвращает кортеж (объект, создан_ли):
tag, created = Tag.objects.get_or_create(name="django")
if created:
print("Новый тег")
Поля для создания, которые не участвуют в поиске, передают в defaults:
author, created = Author.objects.get_or_create(
name="Гоголь",
defaults={"country": "Россия"},
)
update_or_create
Найти и обновить, а если нет — создать:
Book.objects.update_or_create(
title="Мёртвые души",
defaults={"pages": 352, "author": gogol},
)
Эту пару методов использует загрузчик курсов этого сайта: при повторном запуске load_courses уроки обновляются, а не дублируются.
bulk_create и bulk_update
Book.objects.bulk_create([Book(title=f"Том {i}", author=gogol) for i in range(1, 6)])
books = list(Book.objects.filter(author=gogol))
for b in books:
b.pages += 10
Book.objects.bulk_update(books, ["pages"])
Один запрос вместо десятков. Но save() и сигналы не вызываются.
Подсчёт и проверка
Book.objects.count() # SELECT COUNT(*)
Book.objects.filter(pages__gt=500).exists() # True / False
count() или len()?
len(qs) загрузит все объекты в память и посчитает их в Python. qs.count() посчитает в базе. Но если вы всё равно будете перебирать объекты — len() не сделает лишнего запроса.
Агрегация: aggregate()
aggregate считает итог по всему набору и возвращает словарь:
from django.db.models import Avg, Count, Max, Min, Sum
Book.objects.aggregate(Avg("pages"))
# {'pages__avg': 512.4}
Book.objects.aggregate(avg=Avg("pages"), total=Sum("pages"), longest=Max("pages"))
# {'avg': 512.4, 'total': 25620, 'longest': 1300}
Сравните с annotate из прошлого урока: annotate добавляет значение каждому объекту, aggregate — одно значение на весь набор.
Массовые изменения
Book.objects.filter(author=gogol).update(pages=0) # вернёт число изменённых строк
Book.objects.filter(pages=0).delete() # вернёт (число, {модель: число})
Чтобы обновить значение относительно текущего, используют F-выражения:
from django.db.models import F
Post.objects.filter(pk=1).update(views=F("views") + 1) # атомарно, без гонок
Словарь по ключам: in_bulk()
Book.objects.in_bulk([1, 2, 3])
# {1: <Book: ...>, 2: <Book: ...>, 3: <Book: ...>}
Book.objects.in_bulk(["nos", "shinel"], field_name="slug")
Большие объёмы: iterator()
for book in Book.objects.iterator(chunk_size=2000):
process(book)
Объекты читаются порциями и не кэшируются — экономия памяти при обработке миллионов строк.
Сводная таблица
| Метод | Возвращает |
|---|---|
get() |
объект или исключение |
first(), last() |
объект или None |
latest(), earliest() |
объект или исключение |
create() |
созданный объект |
get_or_create(), update_or_create() |
(объект, created) |
bulk_create() |
список объектов |
count() |
число |
exists() |
True / False |
aggregate() |
словарь |
update() |
число строк |
delete() |
кортеж с количеством |
in_bulk() |
словарь |
iterator() |
итератор |
Итог
- Эти методы выполняют запрос сразу и возвращают не QuerySet.
get_or_createиupdate_or_createизбавляют от проверок «есть ли уже такой объект».exists()иcount()выполняются в базе — эффективнее, чем загрузка объектов.aggregate— итог по набору,F— ссылка на значение поля в базе.
Практика решено 0 из 6
Как эффективнее всего проверить, есть ли у автора хотя бы одна книга?
Получите тег с именем django, а если его нет — создайте. Распакуйте результат
в переменные tag и created.
tag, created = Tag.objects.get_or_create(name="django")
Посчитайте среднее и максимальное число страниц среди всех книг одним запросом.
Результат (словарь) сохраните в stats, ключи — avg_pages и max_pages.
from django.db.models import Avg, Max
stats = Book.objects.aggregate(avg_pages=Avg("pages"), max_pages=Max("pages"))
Что вернёт Book.objects.filter(pages__gt=5000).first(), если таких книг нет?
Какие методы не вызывают save() у каждой модели и не отправляют сигналы pre_save/post_save?
Какой метод вернёт самый свежий пост по полю publish? Напишите вызов целиком: Post.objects.???
latest("publish")Войдите, чтобы отмечать пройденные уроки.