Первые ORM запросы (CRUD)
ORM: Python вместо SQL
ORM (Object-Relational Mapping) переводит работу с объектами Python в SQL-запросы. Вместо
SELECT * FROM library_book WHERE author = 'Толстой';
вы пишете
Book.objects.filter(author="Толстой")
Плюсы: не нужно писать SQL, код не зависит от конкретной СУБД, защита от SQL-инъекций встроена.
Django shell
Экспериментировать с ORM удобно в консоли:
python manage.py shell
>>> from library.models import Book
>>> Book.objects.all()
<QuerySet []>
С Django 5.2 модели всех приложений импортируются в shell автоматически.
Менеджер objects
У каждой модели есть менеджер objects — точка входа для запросов. Он возвращает QuerySet — «ленивый» набор объектов из базы.
Create — создание
Способ 1: создать объект и сохранить.
book = Book(title="Война и мир", author="Толстой", pages=1300)
book.save() # INSERT
print(book.id) # id появился после сохранения
Способ 2: одной командой.
book = Book.objects.create(title="Анна Каренина", author="Толстой", pages=860)
Массово:
Book.objects.bulk_create([
Book(title="Отцы и дети", author="Тургенев", pages=290),
Book(title="Муму", author="Тургенев", pages=40),
])
Read — чтение
Book.objects.all() # все книги
Book.objects.filter(author="Толстой") # с условием
Book.objects.exclude(author="Толстой") # все, кроме
Book.objects.get(pk=1) # ровно один объект
Book.objects.filter(author="Толстой").first() # первый или None
Book.objects.count() # количество
Book.objects.order_by("-pages") # сортировка
Book.objects.all()[:5] # первые 5 (LIMIT 5)
get() и исключения
get() ожидает ровно один результат:
try:
book = Book.objects.get(pk=100)
except Book.DoesNotExist:
book = None
Во view для этого есть сокращение get_object_or_404(Book, pk=100) — вернёт объект или страницу 404.
Цепочки и условия
Методы QuerySet можно соединять в цепочки:
Book.objects.filter(author="Толстой").exclude(pages__lt=500).order_by("title")
pages__lt — поисковое выражение (lookup): двойное подчёркивание + оператор. Основные:
| Lookup | Значение | SQL |
|---|---|---|
pages__gt=100 |
больше | > 100 |
pages__gte=100 |
больше или равно | >= 100 |
pages__lt=100 |
меньше | < 100 |
title__contains="мир" |
содержит | LIKE '%мир%' |
title__icontains="мир" |
содержит, без учёта регистра | |
title__startswith="А" |
начинается с | LIKE 'А%' |
author__in=["Толстой", "Чехов"] |
одно из | IN (...) |
published__year=2020 |
год даты |
Подробнее о lookups — в отдельном уроке модуля.
Ленивость
QuerySet не обращается к базе, пока данные действительно не понадобятся (цикл, list(), срез с шагом, len(), вывод в шаблоне). Поэтому цепочки фильтров — это один запрос, а не несколько.
qs = Book.objects.filter(author="Толстой") # запроса ещё нет
qs = qs.order_by("title") # всё ещё нет
for book in qs: # вот теперь — один SELECT
print(book.title)
Update — изменение
Одного объекта:
book = Book.objects.get(pk=1)
book.pages = 1274
book.save() # UPDATE всех полей
book.save(update_fields=["pages"]) # UPDATE только pages
Многих сразу — одним запросом:
Book.objects.filter(author="Толстой").update(author="Л. Н. Толстой")
Delete — удаление
book = Book.objects.get(pk=1)
book.delete()
Book.objects.filter(pages=0).delete() # удалить все подходящие
Осторожно
Book.objects.all().delete() удалит все книги. Django не переспрашивает.
Посмотреть SQL
qs = Book.objects.filter(author="Толстой")
print(qs.query)
Полезно, чтобы понимать, что именно делает ORM.
Итог
objects— менеджер, возвращающий QuerySet.- Create:
create(),save(),bulk_create(). - Read:
all(),filter(),exclude(),get(),first(),count(),order_by(). - Update: изменить атрибут +
save()илиQuerySet.update(). - Delete:
obj.delete()илиQuerySet.delete(). - QuerySet ленивый: запрос выполняется при обращении к данным.
Практика решено 0 из 7
Создайте и сохраните в базе книгу с названием Мастер и Маргарита
и автором Булгаков одной командой через менеджер objects. Результат сохраните в переменную book.
book = Book.objects.create(title="Мастер и Маргарита", author="Булгаков")
Что произойдёт, если Book.objects.get(pk=100) не найдёт книгу?
Получите QuerySet всех книг автора Толстой, кроме тех, у которых меньше 100 страниц
(поле pages). Используйте filter и exclude, результат — в переменную books.
books = Book.objects.filter(author="Толстой").exclude(pages__lt=100)
Расставьте шаги, чтобы изменить название книги с id=3.
-
book.save() -
book.title = "Новое название" -
book = Book.objects.get(pk=3)
Чем Book.objects.filter(pages=0).update(pages=1) лучше цикла с save() для каждой книги?
Какой командой открыть интерактивную консоль Python с подключённым проектом Django?
Найдите книгу по pk=5 и удалите её.
Book.objects.get(pk=5).delete()
Войдите, чтобы отмечать пройденные уроки.