Что такое Manager в Django и для чего он используется

Ответ

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

Основная задача менеджера — предоставлять методы для получения объектов из базы данных. Результатом работы таких методов обычно является QuerySet.

Зачем нужны кастомные менеджеры?

Пользовательские менеджеры создают для двух основных целей:

  1. Добавление новых методов: Для инкапсуляции часто используемых запросов на уровне модели.
  2. Изменение стандартного QuerySet: Для постоянной фильтрации объектов, возвращаемых менеджером.

Пример кастомного менеджера

Допустим, у нас есть модель Post и мы часто хотим получать только опубликованные посты.

from django.db import models
from django.utils import timezone

# 1. Создаем кастомный менеджер
class PublishedManager(models.Manager):
    def get_queryset(self):
        # Переопределяем базовый QuerySet, чтобы он всегда фильтровал по статусу и дате
        return super().get_queryset().filter(
            status='published',
            publish__lte=timezone.now()
        )

class Post(models.Model):
    STATUS_CHOICES = (
        ('draft', 'Draft'),
        ('published', 'Published'),
    )
    title = models.CharField(max_length=250)
    status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
    publish = models.DateTimeField(default=timezone.now)

    # 2. Подключаем менеджеры к модели
    objects = models.Manager()  # Стандартный менеджер
    published = PublishedManager() # Наш кастомный менеджер

    def __str__(self):
        return self.title

Использование

Теперь вместо сложных фильтров в коде можно использовать наш новый менеджер:

# Получить ВСЕ посты, включая черновики
all_posts = Post.objects.all()

# Получить ТОЛЬКО опубликованные посты
# Это короче, чище и менее подвержено ошибкам, чем Post.objects.filter(status='published', ...)
published_posts = Post.published.all()

Ответ 18+ 🔞

А, менеджеры в Джанго! Ну, это же, блядь, как главный завхоз на складе ваших моделей, понимаешь? По умолчанию у каждой модели сидит такой дедок по имени objects, который всё знает, где что лежит, и может тебе любую коробку из базы данных принести.

Но иногда этот дедок — старый, блядь, пердунок, и ему надо либо новые фокусы показать, либо он тебе постоянно суёт в руки не то, что надо. Вот тут и начинается магия кастомных менеджеров.

Зачем они, эти самопальные завхозы?

А вот, блядь, зачем:

  1. Новые трюки в рукав запихнуть. Чтобы не писать один и тот же ебучий фильтр в каждом втором view, можно научить менеджера делать это за тебя. Инкапсуляция, мать её!
  2. Постоянно фильтровать, как параноик. Чтобы менеджер по умолчанию возвращал только то, что нужно, а не всю хуйню подряд. Например, только опубликованные статьи, а не черновики, которые ты в пьяном угаре накодил в три ночи.

Смотри сюда, как это выглядит вживую

Допустим, у нас модель Post (пост, статья, хуита — называй как хочешь). И нам постоянно надо тащить из базы только те, что уже опубликованы. Делать это руками — терпения ноль, ебать. Вот как делают умные люди:

from django.db import models
from django.utils import timezone

# 1. Рожаем своего менеджера. Назовём его PublishedManager, чтоб всё по-взрослому.
class PublishedManager(models.Manager):
    def get_queryset(self):
        # Берем стандартную корзину с объектами (QuerySet) и насильно, блядь, фильтруем.
        # Только 'published' и только те, у кого дата публикации уже наступила.
        return super().get_queryset().filter(
            status='published',
            publish__lte=timezone.now()
        )

class Post(models.Model):
    STATUS_CHOICES = (
        ('draft', 'Черновик (пока никому не показывай, стыдно)'),
        ('published', 'Опубликовано (ура, товарищи!)'),
    )
    title = models.CharField(max_length=250)
    status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
    publish = models.DateTimeField(default=timezone.now)

    # 2. Подключаем менеджеров к модели. Важно не перепутать!
    objects = models.Manager()      # Стандартный дедок. Видит ВСЁ, даже черновики.
    published = PublishedManager()  # Наш параноик. Видит ТОЛЬКО опубликованное.

    def __str__(self):
        return self.title

И как теперь этим пользоваться, спросишь ты?

Да элементарно, Ватсон! Смотри:

# Запросим у старого дедка objects ВСЕ посты. Он притащит и черновики тоже.
all_posts = Post.objects.all()  # Может вернуть какую-нибудь дичь вроде "Мой первый пост про котят (черновик)".

# А теперь спросим у нашего параноика published. Он строгий, блядь.
published_posts = Post.published.all()  # Вернёт только то, что прошло цензуру и готово к показу.

Вот и вся магия. Вместо того чтобы каждый раз писать Post.objects.filter(status='published', publish__lte=timezone.now()) и рисковать ошибиться, ты просто пишешь Post.published.all(). Красота, ёпта! Чисто, понятно, и в коде не надо искать, где ты опять забыл добавить фильтр по дате.