Django ORM: шпаргалка по QuerySet с примерами
Что такое QuerySet в Django и как с ним работать: filter, exclude, get, Q и F-выражения, update, aggregate, annotate, транзакции и типичные ошибки.
Содержание · 16 разделов
- Что такое QuerySet и когда выполняется запрос
- Выборка: all, filter, exclude, get
- Операторы поиска (lookups)
- Сложные условия: Q-объекты
- Сортировка, срезы, уникальность
- Только нужные поля: values, values_list, only
- Создание записей
- Обновление и удаление
- F-выражения: операции на стороне базы
- Агрегация и аннотации
- Связанные объекты без N+1
- Проверка существования и подсчёт
- Транзакции
- Сырой SQL — когда ORM не хватает
- Частые ошибки
- Частые вопросы
- Как получить последнюю запись?
- Как объединить два QuerySet одной модели?
- Как превратить QuerySet в JSON?
- Как подставить значение из другой таблицы подзапросом?
- Можно ли вызывать ORM в асинхронном коде?
Django ORM позволяет работать с базой данных через Python-объекты вместо SQL. Центральное понятие — QuerySet: ленивое описание запроса, которое можно уточнять цепочкой методов и которое выполняется только тогда, когда вам действительно нужны данные.
Один и тот же код ORM работает с SQLite, PostgreSQL, MySQL и другими СУБД: база выбирается в настройке DATABASES, а не в запросах. Как её настроить и когда хватает SQLite — в статье о базах данных в Django, подключение MySQL и MariaDB — в отдельной инструкции по MySQL.
Шпаргалка собрана по частым задачам. Все примеры используют две модели (как описывать поля и связи, читайте в статье «Модели в Django»):
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=200)
country = models.CharField(max_length=2, blank=True)
class Book(models.Model):
title = models.CharField(max_length=300)
author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name="books")
published = models.DateField()
rating = models.DecimalField(max_digits=3, decimal_places=1, default=0)
views = models.PositiveIntegerField(default=0)
Что такое QuerySet и когда выполняется запрос
Book.objects.filter(...) не обращается к базе — он возвращает QuerySet. Запрос выполняется при вычислении:
- итерации:
for book in qs; - приведении к списку или bool:
list(qs),if qs:; - срезе с шагом и обращении по индексу:
qs[0]; - вызовах
len(),repr(), а также методовget(),count(),exists(),first(),aggregate().
Результат вычисленного QuerySet кешируется: повторная итерация по тому же объекту не делает новый запрос. Новый QuerySet, полученный цепочкой (qs.filter(...)), выполнится заново.
qs = Book.objects.filter(rating__gte=4) # запроса нет
books = list(qs) # SELECT ... WHERE rating >= 4
again = list(qs) # из кеша, без запроса
Посмотреть SQL можно через print(qs.query).
Выборка: all, filter, exclude, get
Book.objects.all()
Book.objects.filter(author__country="RU", published__year=2024)
Book.objects.exclude(rating=0)
Book.objects.get(pk=1)
get() возвращает ровно один объект. Если записей нет — исключение Book.DoesNotExist, если больше одной — Book.MultipleObjectsReturned:
try:
book = Book.objects.get(title="Мастер и Маргарита")
except Book.DoesNotExist:
book = None
Если отсутствие записи — нормальная ситуация, проще использовать first(): он вернёт None.
book = Book.objects.filter(title="Мастер и Маргарита").first()
Во view для ответа 404 есть готовый помощник get_object_or_404(Book, pk=pk). Как get_or_create ведёт себя при гонке и чем пользоваться в асинхронном коде — в разборе get() в Django ORM.
Операторы поиска (lookups)
Условия пишутся как поле__оператор=значение:
| Lookup | SQL-аналог | Пример |
|---|---|---|
exact, iexact | = с учётом / без учёта регистра | title__iexact="django" |
contains, icontains | LIKE '%…%' | title__icontains="orm" |
startswith, endswith | LIKE '…%', LIKE '%…' | title__startswith="Django" |
in | IN (…) | pk__in=[1, 2, 3] |
gt, gte, lt, lte | >, >=, <, <= | rating__gte=4 |
range | BETWEEN | published__range=(start, end) |
isnull | IS NULL | author__isnull=True |
year, month, day | извлечение части даты | published__year=2024 |
Через двойное подчёркивание можно идти по связям: Book.objects.filter(author__name__icontains="толстой"), и в обратную сторону — Author.objects.filter(books__rating__gte=4).distinct(). Имя обратной связи задаёт related_name у внешнего ключа; параметры on_delete, null и связь модели с самой собой разобраны в статье о ForeignKey в Django.
Сложные условия: Q-объекты
Несколько аргументов в filter() объединяются через AND. Для OR и NOT используйте Q:
from django.db.models import Q
Book.objects.filter(Q(rating__gte=4.5) | Q(views__gt=10_000))
Book.objects.filter(Q(author__country="RU") & ~Q(rating=0))
Сортировка, срезы, уникальность
Book.objects.order_by("-published", "title") # минус — по убыванию
Book.objects.order_by("?") # случайный порядок (медленно на больших таблицах)
Book.objects.all()[:10] # LIMIT 10
Book.objects.all()[10:20] # LIMIT 10 OFFSET 10
Author.objects.filter(books__rating__gte=4).distinct()
Только нужные поля: values, values_list, only
Book.objects.values("id", "title") # список словарей
Book.objects.values_list("title", flat=True) # плоский список строк
Book.objects.only("id", "title") # модели с отложенной загрузкой остальных полей
values_list(..., flat=True) удобен для получения списка ID или для pk__in. Не злоупотребляйте only()/defer(): обращение к неподгруженному полю вызовет дополнительный запрос на каждый объект.
Создание записей
from datetime import date
book = Book.objects.create(title="Django ORM", author=author, published=date.today())
author, created = Author.objects.get_or_create(name="Лев Толстой", defaults={"country": "RU"})
Author.objects.update_or_create(name="Лев Толстой", defaults={"country": "RU"})
Book.objects.bulk_create(
[Book(title=f"Том {i}", author=author, published=date.today()) for i in range(1, 6)],
batch_size=500,
)
bulk_create вставляет записи пачками, но не вызывает save() и сигналы pre_save/post_save.
Обновление и удаление
# Один UPDATE для всех подходящих строк
Book.objects.filter(rating=0).update(rating=1)
# Обновление одного объекта: сохраняем только изменённые поля
book.title = "Новое название"
book.save(update_fields=["title"])
# Удаление
Book.objects.filter(published__year__lt=2000).delete()
QuerySet.update() и delete() работают на уровне SQL: save() моделей не вызывается, а сигналы pre_save/post_save не срабатывают. (delete() при этом отправляет pre_delete/post_delete и обрабатывает каскады.) Когда выбирать save(), update() или bulk_update() и сколько запросов делает каждый способ — в статье «Как обновить записи в Django».
F-выражения: операции на стороне базы
F() ссылается на значение поля в базе. Это защищает от гонок, когда два процесса одновременно увеличивают счётчик:
from django.db.models import F
Book.objects.filter(pk=book.pk).update(views=F("views") + 1)
F также позволяет сравнивать поля между собой: Book.objects.filter(views__gt=F("rating") * 1000).
Агрегация и аннотации
aggregate() считает итог по всей выборке и возвращает словарь:
from django.db.models import Avg, Count, Max
Book.objects.aggregate(avg_rating=Avg("rating"), total=Count("id"))
# {'avg_rating': Decimal('4.2'), 'total': 120}
annotate() добавляет вычисляемое поле к каждому объекту:
authors = Author.objects.annotate(
books_count=Count("books"),
best_rating=Max("books__rating"),
).filter(books_count__gte=3).order_by("-books_count")
for author in authors:
print(author.name, author.books_count, author.best_rating)
Группировка по полю — комбинация values() и annotate():
Book.objects.values("author__country").annotate(total=Count("id")).order_by("-total")
Связанные объекты без N+1
# ForeignKey и OneToOne — JOIN в одном запросе
Book.objects.select_related("author")
# Обратные связи и ManyToMany — отдельный запрос на связь
Author.objects.prefetch_related("books")
Как находить и устранять лишние запросы, разобрано в статье о проблеме N+1 в Django ORM.
Проверка существования и подсчёт
if Book.objects.filter(author=author).exists(): # SELECT ... LIMIT 1
...
Book.objects.filter(author=author).count() # SELECT COUNT(*)
Не используйте len(qs) только ради количества: он загрузит все строки в память. Но если вы всё равно будете итерироваться по выборке, len() уже вычисленного QuerySet не сделает лишнего запроса.
Транзакции
По умолчанию каждый запрос выполняется в режиме autocommit. Чтобы несколько изменений применились атомарно, используйте transaction.atomic:
from django.db import transaction
with transaction.atomic():
author = Author.objects.create(name="Новый автор")
Book.objects.create(title="Первая книга", author=author, published=date.today())
Если внутри блока возникнет исключение, оба изменения откатятся. Для блокировки строк на время транзакции есть select_for_update().
Сырой SQL — когда ORM не хватает
Book.objects.raw("SELECT * FROM library_book WHERE rating > %s", [4])
Всегда передавайте параметры отдельным списком, а не форматированием строки, — иначе получите SQL-инъекцию.
Частые ошибки
get()без обработки исключений — ошибка 500 вместо 404. Используйтеget_object_or_404во view.- Цикл с
save()вместоupdate()— тысячи запросов там, где хватило бы одного. count()перед итерацией — лишний запрос; если данные всё равно нужны, вычислите выборку один раз.- Фильтрация по связям без
distinct()— при обратных связях строки дублируются. - Обращение к связанным объектам в цикле — классический N+1; добавьте
select_related/prefetch_related.
Полный справочник методов — в документации QuerySet API. Как те же приёмы применяются в админке, показано в руководстве по Django Admin.
Частые вопросы
Как получить последнюю запись?
latest("поле") возвращает объект с максимальным значением поля и бросает DoesNotExist на пустой таблице. last() берёт последний объект с учётом order_by() и на пустой выборке возвращает None.
Book.objects.latest("published")
Book.objects.order_by("published").last()
Как объединить два QuerySet одной модели?
Оператор | склеивает условия через OR и возвращает обычный QuerySet, с которым работают filter() и order_by(). union() выполняет SQL UNION: к результату можно применить срез, count(), exists(), order_by() и values(), а filter() — нет.
Book.objects.filter(rating__gte=5) | Book.objects.filter(author=author)
Book.objects.filter(rating__gte=5).values("title").union(
Book.objects.filter(author=author).values("title")
)
Как превратить QuerySet в JSON?
Возьмите values() с нужными полями и сериализуйте список словарей. Даты и Decimal стандартный json не понимает — передайте DjangoJSONEncoder:
import json
from django.core.serializers.json import DjangoJSONEncoder
data = list(Book.objects.values("id", "title", "published", "rating"))
json.dumps(data, cls=DjangoJSONEncoder, ensure_ascii=False)
# → [{"id": 1, "title": "Война и мир", "published": "1869-01-01", "rating": "5.0"}, ...]
Для API удобнее сериализаторы Django REST Framework.
Как подставить значение из другой таблицы подзапросом?
Subquery с OuterRef добавляет к каждой строке результат вложенного запроса. Пример — название последней книги каждого автора:
from django.db.models import OuterRef, Subquery
newest = Book.objects.filter(author=OuterRef("pk")).order_by("-published")
authors = Author.objects.annotate(last_title=Subquery(newest.values("title")[:1]))
Подзапрос должен возвращать одну колонку и не больше одной строки — отсюда values("title")[:1].
Можно ли вызывать ORM в асинхронном коде?
Да. У методов, которые выполняют запрос, есть асинхронные версии с префиксом a: aget(), afirst(), acount(), aexists(), acreate(), aupdate(), adelete(). По QuerySet можно идти через async for. Транзакции (transaction.atomic) в асинхронном режиме пока не работают — такой код оборачивают в sync_to_async.
count = await Book.objects.filter(author=author).acount()
titles = [book.title async for book in Book.objects.filter(rating__gte=4)]- #Django ORM
- #QuerySet
- #filter
- #annotate
- #Базы данных
- #Оптимизация