Менеджеры

класс Manager

A Manager- это интерфейс, через который для моделей Django предоставляются операции запросов к базе данных. По крайней мере, один Managerсуществует для каждой модели в приложении Django.

Как Managerработают классы, описано в разделе Создание запросов ; этот документ специально касается параметров модели, которые настраивают Manager поведение.

Имена менеджеров

По умолчанию Django добавляет Managerс именем objectsк каждому классу модели Django. Однако, если вы хотите использовать objectsв качестве имени поля или если вы хотите использовать имя, отличное от objectsимени Manager, вы можете переименовать его для каждой модели. Чтобы переименовать Managerдля данного класса, определите атрибут класса типа models.Manager()в этой модели. Например:

from django.db import models

class Person(models.Model):
    #...
    people = models.Manager()

Используя этот пример модели, Person.objectsбудет сгенерировано AttributeErrorисключение, но Person.people.all()будет предоставлен список всех Personобъектов.

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

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

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

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

Добавление дополнительных Managerметодов - это предпочтительный способ добавить к вашим моделям функциональность на уровне таблиц. (Для функциональности «на уровне строк», т. Е. Функций, которые действуют на один экземпляр объекта модели, используйте методы модели , а не пользовательские Managerметоды.)

Например, этот обычай Managerдобавляет метод with_counts():

from django.db import models
from django.db.models.functions import Coalesce

class PollManager(models.Manager):
    def with_counts(self):
        return self.annotate(
            num_responses=Coalesce(models.Count("response"), 0)
        )

class OpinionPoll(models.Model):
    question = models.CharField(max_length=200)
    objects = PollManager()

class Response(models.Model):
    poll = models.ForeignKey(OpinionPoll, on_delete=models.CASCADE)
    # ...

В этом примере вы будете использовать , OpinionPoll.objects.with_counts()чтобы получить QuerySetиз OpinionPollобъектов с дополнительным num_responses атрибутом прилагается.

Пользовательский Managerметод может возвращать все, что вы хотите. Необязательно возвращать QuerySet.

Также следует отметить, что Managerметоды могут получать доступ self.modelк классу модели, к которому они прикреплены.

Изменение инициала менеджера QuerySet

ManagerБаза A QuerySetвозвращает все объекты в системе. Например, используя эту модель:

from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)

… Оператор Book.objects.all()вернет все книги в базе данных.

Вы можете переопределить Managerбазу a QuerySet, переопределив Manager.get_queryset()метод. get_queryset()должен вернуть QuerySetс нужными вам свойствами.

Например, в следующей модели есть два Manager s: один возвращает все объекты, а другой - только книги Роальда Даля:

# First, define the Manager subclass.
class DahlBookManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(author='Roald Dahl')

# Then hook it into the Book model explicitly.
class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)

    objects = models.Manager() # The default manager.
    dahl_objects = DahlBookManager() # The Dahl-specific manager.

В этом примере модели Book.objects.all()будут возвращены все книги в базе данных, но Book.dahl_objects.all()будут возвращены только те, которые написаны Роальдом Далем.

Потому что get_queryset()возвращает QuerySetобъект, вы можете использовать filter(), exclude()и все другие QuerySetметоды на него. Итак, все эти утверждения законны:

Book.dahl_objects.all()
Book.dahl_objects.filter(title='Matilda')
Book.dahl_objects.count()

В этом примере также отмечен еще один интересный прием: использование нескольких менеджеров в одной модели. Вы можете прикрепить Manager()к модели столько экземпляров, сколько захотите. Это неповторяющийся способ определения общих «фильтров» для ваших моделей.

Например:

class AuthorManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(role='A')

class EditorManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(role='E')

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    role = models.CharField(max_length=1, choices=[('A', _('Author')), ('E', _('Editor'))])
    people = models.Manager()
    authors = AuthorManager()
    editors = EditorManager()

Этот пример позволяет запрашивать Person.authors.all(), Person.editors.all()и Person.people.all(), получая предсказуемые результаты.

Менеджеры по умолчанию

Model._default_manager

Если вы используете настраиваемые Managerобъекты, обратите внимание, что первые Manager встречи Django (в том порядке, в котором они определены в модели) имеют особый статус. Django интерпретирует первое, Managerопределенное в классе, как «значение по умолчанию» Manager, и несколько частей Django (в том числе dumpdata) будут использовать его Managerисключительно для этой модели. В результате рекомендуется быть осторожным при выборе диспетчера по умолчанию, чтобы избежать ситуации, когда переопределение get_queryset()приводит к невозможности получить объекты, с которыми вы хотите работать.

Вы можете указать собственный менеджер по умолчанию, используя Meta.default_manager_name.

Если вы пишете код, который должен обрабатывать неизвестную модель, например, в стороннем приложении, реализующем общее представление, используйте этот менеджер (или _base_manager) вместо того, чтобы предполагать, что у модели есть objects менеджер.

Базовые менеджеры

Model._base_manager

Не отфильтровывайте результаты в этом типе подкласса менеджеров

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

Следовательно, не следует переопределять get_queryset()фильтрацию каких-либо строк. Если вы это сделаете, Django вернет неполные результаты.

Вызов кастомных QuerySetметодов из менеджера

Хотя большинство методов из стандарта QuerySetдоступны непосредственно из Manager, это относится только к дополнительным методам, определенным на заказ, QuerySetесли вы также реализуете их на Manager:

class PersonQuerySet(models.QuerySet):
    def authors(self):
        return self.filter(role='A')

    def editors(self):
        return self.filter(role='E')

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

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

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

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    role = models.CharField(max_length=1, choices=[('A', _('Author')), ('E', _('Editor'))])
    people = PersonManager()

Этот пример позволяет вам звонить как напрямую, так authors()и editors()от менеджера Person.people.

Создание менеджера с помощью QuerySetметодов

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

class Person(models.Model):
    ...
    people = PersonQuerySet.as_manager()

ManagerЭкземпляр , созданный QuerySet.as_manager()будет практически идентичен PersonManagerиз предыдущего примера.

Не каждый QuerySetметод имеет смысл на Managerуровне; например, мы намеренно предотвращаем QuerySet.delete()копирование метода в Managerкласс.

Методы копируются по следующим правилам:

  • Публичные методы копируются по умолчанию.
  • Приватные методы (начинающиеся с подчеркивания) по умолчанию не копируются.
  • Всегда копируются методы с queryset_onlyатрибутом, установленным на False.
  • Методы с queryset_onlyатрибутом, установленным на True, никогда не копируются.

Например:

class CustomQuerySet(models.QuerySet):
    # Available on both Manager and QuerySet.
    def public_method(self):
        return

    # Available only on QuerySet.
    def _private_method(self):
        return

    # Available only on QuerySet.
    def opted_out_public_method(self):
        return
    opted_out_public_method.queryset_only = True

    # Available on both Manager and QuerySet.
    def _opted_in_private_method(self):
        return
    _opted_in_private_method.queryset_only = False

from_queryset()

classmethodfrom_queryset ( queryset_class )

Для расширенного использования вам могут понадобиться и custom, Managerи custom QuerySet. Вы можете сделать это, вызвав, Manager.from_queryset()который возвращает подкласс вашей базы Managerс копией пользовательских QuerySetметодов:

class CustomManager(models.Manager):
    def manager_only_method(self):
        return

class CustomQuerySet(models.QuerySet):
    def manager_and_queryset_method(self):
        return

class MyModel(models.Model):
    objects = CustomManager.from_queryset(CustomQuerySet)()

Вы также можете сохранить сгенерированный класс в переменной:

MyManager = CustomManager.from_queryset(CustomQuerySet)

class MyModel(models.Model):
    objects = MyManager()

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

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

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

Эти правила обеспечивают необходимую гибкость, если вы хотите установить коллекцию настраиваемых менеджеров в группе моделей через абстрактный базовый класс, но при этом настроить менеджер по умолчанию. Например, предположим, что у вас есть этот базовый класс:

class AbstractBase(models.Model):
    # ...
    objects = CustomManager()

    class Meta:
        abstract = True

Если вы используете это непосредственно в подклассе, objectsбудет менеджером по умолчанию, если вы не объявите менеджеров в базовом классе:

class ChildA(AbstractBase):
    # ...
    # This class has CustomManager as the default manager.
    pass

Если вы хотите наследовать AbstractBase, но предоставить другой менеджер по умолчанию, вы можете предоставить менеджер по умолчанию для дочернего класса:

class ChildB(AbstractBase):
    # ...
    # An explicit default manager.
    default_manager = OtherManager()

Вот значение default_managerпо умолчанию. objectsМенеджер по - прежнему доступен, так как он унаследовал, но не используется в качестве значения по умолчанию.

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

class ExtraManager(models.Model):
    extra_manager = OtherManager()

    class Meta:
        abstract = True

class ChildC(AbstractBase, ExtraManager):
    # ...
    # Default manager is CustomManager, but OtherManager is
    # also available via the "extra_manager" attribute.
    pass

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

ClassA.objects.do_something()

законно, но:

AbstractBase.objects.do_something()

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

Проблемы реализации

Какие бы функции вы ни добавляли в свой заказ Manager, должна быть возможность сделать неглубокую копию Managerэкземпляра; т.е. должен работать следующий код:

>>> import copy
>>> manager = MyManager()
>>> my_copy = copy.copy(manager)

Django делает мелкие копии объектов-менеджеров во время определенных запросов; если ваш менеджер не может быть скопирован, эти запросы не будут выполнены.

Для большинства кастомных менеджеров это не проблема. Если вы просто добавляете простые методы в свой Manager, маловероятно, что вы случайно сделаете экземпляры своих Managerнекопируемых. Однако, если вы переопределяете __getattr__или какой-либо другой частный метод вашего Managerобъекта, который контролирует состояние объекта, вы должны убедиться, что вы не повлияете на возможность вашего Managerкопирования.

Copyright ©2021 All rights reserved