Продвинутый Django 5 для продолжающих

Прогресс по курсу:  0/193

2.12 Пользовательские менеджеры моделей
2 из 2 шагов пройдено

Пользовательский менеджер моделей Django

Вы можете использовать собственный менеджер моделей в Django в конкретной модели, расширив класс Manager и создав экземпляр своего собственного менеджера в своей модели.

Есть две причины, которые могут расширить класс Manager: добавить дополнительный метод менеджера или изменить исходный QuerySet, возвращаемый менеджером.

Добавление дополнительного метода менеджера

Здесь я собираюсь добавить пользовательский метод менеджера, который отвечает за получение всех сотрудников возрастом старше 30 лет из таблицы Employee. Чтобы добавить дополнительный метод менеджера, мы должны расширить класс и определить метод внутри того класса, который мы хотим создать models.Manager

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

Этот пользовательский менеджер добавляет метод get_age().

class CustomManager(models.Manager):
    def get_age(self):
        return self.filter(age__gt=30)

class Employee(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    age = models.SmallIntegerField(null=True)
    created = models.DateTimeField(default=timezone.now)
    work_experience = models.SmallIntegerField(default=0)
    contact = models.OneToOneField(Contact, on_delete=models.CASCADE, null=True)
    department = models.ForeignKey(Department, on_delete=models.CASCADE, default=None)
    compensations = models.ManyToManyField(Compensation)
    persons = CustomManager()

    def __str__(self):
        return f'{self.first_name} {self.last_name}'

Таким образом, мы успешно добавили пользовательский менеджер моделей CustomManager вместе с методом get_age(). Теперь вы можете получить всех сотрудников, старше 30 лет, используя Employee.persons.get_age() оператор.

Давайте разберемся, что такое начальный QuerySet, для использования модели Employee, когда вы используете Employee.objects.all()оператор, он возвращает QuerySet (экземпляры моделей Employee), и этот QuerySet называется начальным QuerySet.

Базовый запрос менеджера по умолчанию возвращает все объекты модели, но мы можем это изменить. Метод get_queryset() вернет QuerySet вместе с требуемыми свойствами. Давайте сделаем так, чтобы при обращении к Employee.persons.all() мы выводили список сотрудников, которые относятся к департаменту = 1, в нашем случае этот департамент называется "IT"

class CustomManager(models.Manager):
    def get_age(self):
        return self.filter(age__gt=30)

    def get_queryset(self):
        return super().get_queryset().filter(department=1)


class Employee(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    age = models.SmallIntegerField(null=True)
    created = models.DateTimeField(default=timezone.now)
    work_experience = models.SmallIntegerField(default=0)
    contact = models.OneToOneField(Contact, on_delete=models.CASCADE, null=True)
    department = models.ForeignKey(Department, on_delete=models.CASCADE, default=None)
    compensations = models.ManyToManyField(Compensation)
    objects = models.Manager()
    persons = CustomManager()

    def __str__(self):
        return f'{self.first_name} {self.last_name}'

В приведенной выше модели Django есть два менеджера: persons возвращает всех сотрудников, исходя из их фильтрации по департаменту, а objects возвращает все экземпляры сотрудников.

Пользовательский метод QuerySet

Здесь вы должны помнить один самый важный момент: метод Employee.objects.all()  возвращает QuerySet, который является экземпляром класса QuerySet.

Класс QuerySet, написанный внутри пакета django.db.models.

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

Для этого примера я добавлю два метода: get_experience() для получения только опытных сотрудников и get_not_experienced() для получения только сотрудников с низким опытом.

class EmployeeQuerySet(models.QuerySet):
    def get_experience(self):
        return self.filter(work_experience__gt=3)

    def get_not_experienced(self):
        return self.filter(work_experience__lt=3)


class CustomManager(models.Manager):
    def get_queryset(self):
        return EmployeeQuerySet(self.model, using=self._db)

    def get_experience(self):
        return self.get_queryset().get_experience()

    def get_not_experienced(self):
        return self.get_queryset().get_not_experienced()


class Employee(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    age = models.SmallIntegerField(null=True)
    created = models.DateTimeField(default=timezone.now)
    work_experience = models.SmallIntegerField(default=0)
    contact = models.OneToOneField(Contact, on_delete=models.CASCADE, null=True)
    department = models.ForeignKey(Department, on_delete=models.CASCADE, default=None)
    compensations = models.ManyToManyField(Compensation)
    persons = CustomManager()

    def __str__(self):
        return f'{self.first_name} {self.last_name}'

Этот пример позволяет вам вызывать и get_experience() и get_not_experienced() непосредственно из менеджера Employee.persons c пользовательским QuerySet.

 

Пользовательские менеджеры и наследование моделей 

Вот как Django обрабатывает пользовательские менеджеры и наследование моделей :

  1. Менеджеры из базовых классов всегда наследуются дочерним классом, используя обычный порядок разрешения имен Python (имена в дочернем классе переопределяют все остальные; затем идут имена в первом родительском классе и так далее).
  2. Если для модели и/или ее родителей не объявлены менеджеры, Django автоматически создает objects менеджер.
  3. Менеджер по умолчанию для класса — это либо тот, который выбран с помощью Meta.default_manager_name, либо первый менеджер, объявленный в модели, либо менеджер по умолчанию первой родительской модели.

  • Комментария
Будьте вежливы и соблюдайте наши принципы сообщества. Пожалуйста, не оставляйте решения и подсказки в комментариях, для этого есть отдельный форум.
Оставить комментарий

В классе CustomManager в чем необходимость переопределять методы и вызывать из него методы  EmployeeQuerySet? Почему сразу нельзя их определить в CustomManager? И что значит using=self._db?

@Георгий_Тимофеев, Тут показан пример как добавить пользовательский метод в QuerySet, чтобы можно было вызвать через кастомный метод. Если его не указать в менеджере, то мы получим ошибку, что метод не найден, например  AttributeError: 'CustomManager' object has no attribute 'get_experience'

using=self._db означает что используем бд "по умолчанию", по умолчанию мы должны обязательно передать нашу модель и какую бд будем использовать. По умолчанию в инициализации класса QuerySet параметры: model=None, using=None

 

Жаль что на такой огромный второй раздел ни одного практического задания, а тут есть что попрактиковать

@Георгий_Тимофеев, на старом проверочном коде(тот, который использовался на первом курсе) не сделать сложные задачи на орм, а новый требует существенного времени на разработку. Именно на нём будут сделаны задачи в этом разделе, а затем и переведены на него орм-задачи первого курса.