Затрудняюсь понять полезность пользовательских менеджеров моделей Django

У меня нет кода для этого вопроса, поэтому речь пойдет скорее о полезности менеджеров по работе с клиентами, чем о реализации.

Я прочитал документацию, много постов в блогах и попытался реализовать некоторые из них самостоятельно, но я не могу найти полезность в пользовательских менеджерах моделей Django. Я понимаю, что они могут помочь сегментировать код и могут помочь принципам DRY, но мне трудно понять как. Поэтому этот вопрос можно разбить на несколько подпунктов

  • How does this help with DRY principles?
  • How does this help with code segmentation?
  • What can managers do that views or model methods cannot do?

Вы можете инкапсулировать фильтрацию, аннотирование, подзапросы и т.д. в менеджере. Например:

class ActiveManager(models.Manager):

    def get_queryset(self, *args, **kwargs):
        return super().get_queryset(*args, **kwargs).filter(
            active=True
        )

и затем применить это к модулю с полем active:

class MyActiveModel(models.Model):
    active = models.BooleanField(default=False)

    objects = ActiveManager()

Теперь, если мы используем MyActiveModel.objects.all(), он будет извлекать только записи с active = True. Таким образом, нам не нужно применять эту фильтрацию во всех представлениях, мы можем использовать objects как в любой модели, и она будет прозрачно фильтровать объект.

А Manager также часто используется, если вы хотите сделать расширение кверисета.

Например, мы можем определить AuthorQuerySet, который предлагает дополнительный метод .of_user(…), который, например, сохраняет записи, где данный пользователь является автором, или пользователь является суперпользователем, получить все записи с помощью:

class AuthorQuerySet(models.QuerySet):

    def from_user(self, user):
        if user.is_superuser:
            return self
        else:
            return self.filter(author=user)

и затем работать с менеджером, который позволяет делать такую фильтрацию:

from django.conf import settings

class Post(models.Model):
    author = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
    )

    objects = AuthorQuerySet.as_manager()

тогда мы можем получить объекты, которые пользователь может видеть с помощью:

Post.objects.filter_user(myuser)

или сделать это в цепочке фильтров:

Post.objects.filter(title__istartswith='The').filter_user(myuser)

Таким образом, он в основном занимается инкапсуляцией логики, так что фильтрация, аннотирование и т.д. определяются только один раз и затем используются в представлениях прозрачным образом. Другими словами, представлению не нужно беспокоиться о том, что оно получит только активные объекты, об этом позаботится менеджер.

Как это поможет в сегментации кода?

Модель фокусируется на хранении записи в базе данных и представлении данных. Обычно она должна не решать, какие записи вы видите в той или иной ситуации. Для этого используются views. Но если логика одна и та же, то нет смысла писать это в представлениях, так как это делает код склонным к ошибкам в случае, если фильтрация, аннотирование и т.д., которые (почти) всегда должны происходить, в этом случае копируются в большое количество представлений. Реализуя это в менеджере, мы получаем прослойку между моделями и представлениями, которая может фильтровать, аннотировать и т.д. данные всегда одним и тем же способом.

Что могут делать менеджеры, чего не могут делать представления или методы модели?

Можно повторить один и тот же код фильтрации один раз для каждого представления, но это затруднит изменение этой логики в дальнейшем процессе, поскольку тогда нужно будет смотреть, в каких представлениях происходит обращение к этим моделям, и вносить изменения во все эти представления. В этом случае легко упустить что-то из виду и в итоге получить непоследовательное состояние.

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

Другими словами, это слой между моделью и представлением, и он работает по принципу "многие-к-одному" для модели и "многие-ко-многим" для кода представления.

Я постараюсь кратко ответить на ваш вопрос, оставив место для более глубоких ответов другим комментаторам. Также нет кода.

Как это помогает принципам DRY?

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

  • определить их в одном месте и повторно использовать в любом другом
  • модифицировать их функциональность в одном месте без необходимости заботиться о поиске & модифицировать места, где вы фактически используете метод менеджера (запрос)

В принципе, следуя этому принципу (в вашем случае с пользовательскими менеджерами), вы уменьшаете сложность кодовой базы, с которой вам приходится работать.

Как это поможет в сегментации кода?

Все связанные с запросами вещи живут в менеджере соответствующей модели - вы не перемешиваете представление и бизнес-логику вместе с некоторыми утилитами по всей кодовой базе. Метод менеджера ничего не знает о своих потребителях, а потребителям не нужно ничего знать о реализации этих методов.

Что могут делать менеджеры, чего не могут делать представления или методы модели?

В менеджерах вы можете изменить возвращаемый набор запросов, и он будет изменен по умолчанию для каждого потребителя. Также, говоря о менеджерах и моделях, есть семантическое различие: методы модели предполагают, что они работают конкретно над данными, хранящимися в экземпляре модели или над чем-то относительно любого экземпляра модели, в то время как менеджеры работают над любыми неспецифическими сущностями (но той же модели). Думайте об этом так: "методы модели делают что-то над строкой таблицы, а менеджеры делают что-то над строками таблицы".

Вернуться на верх