Затрудняюсь понять полезность пользовательских менеджеров моделей Django
У меня нет кода для этого вопроса, поэтому речь пойдет скорее о полезности менеджеров по работе с клиентами, чем о реализации.
Я прочитал документацию, много постов в блогах и попытался реализовать некоторые из них самостоятельно, но я не могу найти полезность в пользовательских менеджерах моделей Django. Я понимаю, что они могут помочь сегментировать код и могут помочь принципам DRY, но мне трудно понять как. Поэтому этот вопрос можно разбить на несколько подпунктов
- How does this help with DRY principles?
- How does this help with code segmentation?
- What can managers do that
views
ormodel
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?
Обычно, и особенно в больших проектах, у вас есть набор запросов, которые (повторно) широко используются в коде. Например, получение списка покупок для пользователя или получение некоторых мета-данных учетной записи с аннотированными полями. В случае хранения этих запросов в менеджере пользовательских моделей вы можете:
- определить их в одном месте и повторно использовать в любом другом
- модифицировать их функциональность в одном месте без необходимости заботиться о поиске & модифицировать места, где вы фактически используете метод менеджера (запрос)
В принципе, следуя этому принципу (в вашем случае с пользовательскими менеджерами), вы уменьшаете сложность кодовой базы, с которой вам приходится работать.
Как это поможет в сегментации кода?
Все связанные с запросами вещи живут в менеджере соответствующей модели - вы не перемешиваете представление и бизнес-логику вместе с некоторыми утилитами по всей кодовой базе. Метод менеджера ничего не знает о своих потребителях, а потребителям не нужно ничего знать о реализации этих методов.
Что могут делать менеджеры, чего не могут делать представления или методы модели?
В менеджерах вы можете изменить возвращаемый набор запросов, и он будет изменен по умолчанию для каждого потребителя. Также, говоря о менеджерах и моделях, есть семантическое различие: методы модели предполагают, что они работают конкретно над данными, хранящимися в экземпляре модели или над чем-то относительно любого экземпляра модели, в то время как менеджеры работают над любыми неспецифическими сущностями (но той же модели). Думайте об этом так: "методы модели делают что-то над строкой таблицы, а менеджеры делают что-то над строками таблицы".