📖 Теория: ORM запросы в Django

К оглавлению урока

⚡ Кратко

Менеджер objects — стандартный интерфейс Django ORM. Методы возвращают QuerySet — ленивый набор записей.

  • Создание: Model.objects.create() или obj = Model(); obj.save()
  • Чтение: .all(), .filter(), .exclude(), .get()
  • Lookups: field__icontains, field__gt, field__in, field__range
  • Q-объекты: Q(a=1) | Q(b=2) — OR; Q(a=1) & Q(b=2) — AND; ~Q(a=1) — NOT
  • get(): бросает DoesNotExist или MultipleObjectsReturned

1. Менеджер моделей

В Django ORM менеджер моделей — это интерфейс для выполнения запросов к базе данных для конкретной модели. Менеджеры отвечают за создание, фильтрацию, обновление и удаление записей.

Менеджер по умолчанию
Каждая модель Django имеет встроенный менеджер objects. Он предоставляет доступ ко всем методам QuerySet API.

Основные группы методов objects

ГруппаМетоды
Созданиеcreate(), bulk_create()
Чтениеall(), filter(), exclude(), get(), count(), exists()
Обновлениеupdate()
Удалениеdelete()

2. Django Shell

Django Shell — интерактивная Python-оболочка, настроенная на ваш проект. Позволяет выполнять ORM-запросы прямо из терминала без написания view или скриптов.

Как запустить

# Запуск Django Shell
python manage.py shell

Работа в Django Shell

# 1. Импорт моделей
from store.models import Book  # конкретная модель
# или
from store.models import *     # все модели приложения

# 2. Выполнение ORM-запросов
all_books = Book.objects.all()
print(all_books)
Совет
Используйте python manage.py shell_plus (пакет django-extensions) — он автоматически импортирует все модели.

3. QuerySet

QuerySet — объект, представляющий набор данных из базы данных. Основные свойства:

  • Ленивость (lazy): SQL-запрос выполняется только когда данные реально нужны (итерация, приведение к списку, срез)
  • Цепочки (chaining): методы можно объединять — .filter().order_by().values()
  • Кешируемость: после первого выполнения результат кешируется внутри QuerySet
# QuerySet ленивый — запрос ещё не отправлен
qs = Book.objects.filter(author="Orwell")

# Запрос выполняется здесь — при итерации
for book in qs:
    print(book.title)

4. Создание записей

Метод create()

Создаёт и сохраняет новую запись в БД за один шаг:

from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)
    published_date = models.DateField()
    price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)

# Создание новой книги
new_book = Book.objects.create(
    title="The Great Gatsby",
    author="F. Scott Fitzgerald",
    published_date="1925-04-10",
    price=15
)
# Объект автоматически сохранён в БД
print(new_book.id)  # ID присвоен базой данных

Метод save()

Даёт возможность изменить объект до сохранения:

# Создание экземпляра без сохранения
new_book = Book(
    title="1984",
    author="George Orwell",
    published_date="1949-06-08",
    price=20
)

# Изменение до сохранения
new_book.title = "Nineteen Eighty-Four"

# Явное сохранение в БД
new_book.save()
print(new_book.id)
create()save()
Шагов12+
Когда использоватьПростое созданиеНужна обработка перед сохранением
SQLINSERTINSERT или UPDATE

5. Чтение записей

Метод all()

Возвращает QuerySet со всеми записями таблицы:

all_books = Book.objects.all()
for book in all_books:
    print(book.title)

Метод first() и last()

Возвращают первую/последнюю запись или None если QuerySet пуст:

first_book = Book.objects.all().first()
if first_book:
    print(f"Первая книга: {first_book.title}")

last_book = Book.objects.all().last()
if last_book:
    print(f"Последняя книга: {last_book.title}")

Метод count()

Возвращает количество записей (выполняет SELECT COUNT(*)):

book_count = Book.objects.all().count()
print(f"Количество книг: {book_count}")

Метод exists()

Возвращает True/False — есть ли хотя бы одна запись. Эффективнее, чем count() > 0:

has_books = Book.objects.all().exists()
print(f"Книги существуют: {has_books}")

Метод values()

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

books_values = Book.objects.all().values('title', 'author')
for book in books_values:
    print(f"Название: {book['title']}, Автор: {book['author']}")

6. Метод get()

Используется для получения ровно одной записи по заданным критериям:

from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned

# Поиск по одному полю
try:
    book = Book.objects.get(title="The Great Gatsby")
    print(f"Книга найдена: {book.title}")
except ObjectDoesNotExist:
    print("Книга не найдена.")
except MultipleObjectsReturned:
    print("Найдено несколько книг с одинаковым названием.")

# Поиск по нескольким полям
try:
    book = Book.objects.get(title="The Great Gatsby", author="F. Scott Fitzgerald")
    print(f"Книга найдена: {book.title}")
except ObjectDoesNotExist:
    print("Книга не найдена.")
except MultipleObjectsReturned:
    print("Найдено несколько книг.")
Исключения метода get()
  • Model.DoesNotExist (или ObjectDoesNotExist) — запись не найдена
  • MultipleObjectsReturned — найдено более одной записи
Используйте get() только для поиска по уникальным полям (например, id, email).

7. Фильтры: filter() и exclude()

Основные методы выборки по условию. filter() выбирает подходящие, exclude() — исключает подходящие:

# filter() — выбрать записи, соответствующие условию
bestsellers = Book.objects.filter(is_bestseller=True)

# exclude() — исключить записи, соответствующие условию
non_bestsellers = Book.objects.exclude(is_bestseller=True)

# Можно цеплять
recent_bestsellers = Book.objects.filter(
    is_bestseller=True
).exclude(
    published_date__lt="2000-01-01"
)

8. Lookups — условия фильтрации

Lookups — механизм построения условий через синтаксис поле__условие=значение. Используются в filter(), exclude(), get().

Разделитель — двойное подчёркивание __. Например: author__icontains='john' → «поле author содержит 'john' без учёта регистра».

Точное совпадение

# exact — точное совпадение (регистрозависимо)
books = Book.objects.filter(title__exact="The Great Gatsby")

# iexact — точное совпадение без учёта регистра
books = Book.objects.filter(title__iexact="the great gatsby")

Поиск подстроки

# contains — содержит подстроку (регистрозависимо)
books = Book.objects.filter(title__contains="Gatsby")

# icontains — содержит подстроку (без учёта регистра)
books = Book.objects.filter(title__icontains="gatsby")

Начало и конец строки

# startswith / istartswith
books = Book.objects.filter(title__startswith="The")
books = Book.objects.filter(title__istartswith="the")

# endswith / iendswith
books = Book.objects.filter(title__endswith="Gatsby")
books = Book.objects.filter(title__iendswith="gatsby")

Список значений (in)

# in — значение входит в список
books = Book.objects.filter(id__in=[1, 2, 3])

Сравнение чисел и дат

# gt — больше; gte — больше или равно
# lt — меньше; lte — меньше или равно
books = Book.objects.filter(published_date__gt="2000-01-01")
books = Book.objects.filter(published_date__gte="2000-01-01")
books = Book.objects.filter(published_date__lt="2000-01-01")
books = Book.objects.filter(published_date__lte="2000-01-01")

Проверка на NULL

# isnull — поле равно NULL
books = Book.objects.filter(published_date__isnull=True)
books = Book.objects.filter(published_date__isnull=False)

Диапазон значений

# range — значение в диапазоне (включая границы)
books = Book.objects.filter(
    published_date__range=["2022-01-01", "2023-01-01"]
)

9. Класс Q — сложные логические условия

Класс Q позволяет строить запросы с логическими операторами AND, OR, NOT — то, что невозможно выразить через обычный filter().

from django.db.models import Q

# AND (&) — оба условия должны быть истинны
books = Book.objects.filter(
    Q(is_bestseller=True) & Q(published_date__gt="2000-01-01")
)

# OR (|) — хотя бы одно условие истинно
books = Book.objects.filter(
    Q(is_bestseller=True) | Q(published_date__gt="2000-01-01")
)

# NOT (~) — условие ложно
books = Book.objects.filter(~Q(is_bestseller=True))

# Комбинация: (бестселлер ИЛИ после 2000) И НЕ Оруэлл
books = Book.objects.filter(
    (Q(is_bestseller=True) | Q(published_date__gt="2000-01-01"))
    & ~Q(author="George Orwell")
)
Совет
Простые AND-условия можно писать через filter(a=1, b=2) — без Q. Класс Q нужен, когда требуется OR или NOT.
⚠️ Проверить по документации: в Django есть асинхронный QuerySet API — методы вроде aget(), acreate(), acount(), aaggregate() и асинхронная итерация (async for obj in qs). Отдельного afilter() нет: сам filter() ленивый и остаётся синхронным (он не обращается к БД до материализации). Для синхронного кода всё описанное выше актуально. См. официальную документацию.