Работа с наборами запросов QuerySet и менеджерами
Запросы к блогу в shell
Добавьте через админку несколько постов (часть — черновики, часть — опубликованные) и откройте консоль:
python manage.py shell
>>> from django.contrib.auth.models import User
>>> from blog.models import Post
>>> user = User.objects.get(username="admin")
>>> post = Post.objects.create(title="Ещё один пост", slug="eshhe-odin-post", body="Текст", author=user)
>>> Post.objects.all()
>>> Post.objects.filter(publish__year=2026)
>>> Post.objects.filter(publish__year=2026, author__username="admin")
>>> Post.objects.filter(status=Post.Status.PUBLISHED).exclude(title__startswith="Ещё")
>>> Post.objects.order_by("title")
>>> Post.objects.get(id=1).delete()
Всё это вы уже умеете из модуля про ORM — теперь применяем к реальной модели.
Проблема: везде нужен фильтр по статусу
На сайте читателям показываются только опубликованные посты. Значит, в каждом view придётся писать:
Post.objects.filter(status=Post.Status.PUBLISHED)
Забыли в одном месте — читатели видят черновики. Решение — собственный менеджер.
Менеджеры
Менеджер — объект, через который модель обращается к базе. objects — менеджер по умолчанию. Можно создать свой, переопределив get_queryset():
class PublishedManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status=Post.Status.PUBLISHED)
class Post(models.Model):
# ... поля ...
objects = models.Manager() # менеджер по умолчанию
published = PublishedManager() # наш менеджер
class Meta:
...
Теперь:
Post.published.all()
Post.published.filter(title__startswith="Как")
Post.published.count()
Порядок и objects
Если в модели объявлен хотя бы один свой менеджер, Django не создаёт objects автоматически — объявите его явно. Первый объявленный менеджер становится менеджером по умолчанию (его использует, например, админка), поэтому objects ставим первым.
Методы менеджера и QuerySet
Кроме фильтрации по умолчанию, в менеджер можно добавить свои методы:
class PostQuerySet(models.QuerySet):
def published(self):
return self.filter(status=Post.Status.PUBLISHED)
def by_author(self, username):
return self.filter(author__username=username)
class Post(models.Model):
...
objects = PostQuerySet.as_manager()
Post.objects.published().by_author("admin") # методы можно соединять в цепочку!
Это ещё одно проявление принципа «толстые модели»: логика выборки живёт рядом с моделью, а не размазана по view.
Ещё раз о ленивости
posts = Post.published.all() # запроса нет
posts = posts.filter(publish__year=2026) # запроса нет
for post in posts: # SELECT ... — один раз
print(post.title)
for post in posts: # повторно не запрашивает: результаты закэшированы
print(post.slug)
QuerySet выполняется при: итерации, срезе с шагом, len(), list(), bool(), repr() (в shell), pickle. Результат кэшируется в объекте QuerySet.
Итог
- Собственный менеджер инкапсулирует частые фильтры:
Post.published.all(). - Переопределяйте
get_queryset(), вызываяsuper(). - При объявлении своего менеджера явно добавьте
objects = models.Manager()первым. QuerySet.as_manager()позволяет создавать цепочки своих методов.
Практика решено 0 из 5
Напишите менеджер PublishedManager, который переопределяет get_queryset()
и возвращает только посты со статусом Post.Status.PUBLISHED.
class PublishedManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status=Post.Status.PUBLISHED)
Подключите к модели Post два менеджера: стандартный objects и published
(экземпляр PublishedManager). Стандартный должен идти первым.
class Post(models.Model):
# ... поля ...
objects = models.Manager()
published = PublishedManager()
Почему после добавления published = PublishedManager() нужно явно объявить objects = models.Manager()?
Получите опубликованные посты автора с именем пользователя admin,
в заголовке которых есть слово Django (без учёта регистра). Результат — в posts.
posts = Post.published.filter(author__username="admin", title__icontains="django")
Когда выполнится SQL-запрос в коде qs = Post.published.all(); qs = qs.filter(title__startswith="A"); print(qs.count())?
Войдите, чтобы отмечать пройденные уроки.