Менеджеры ¶
-
класс
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()
¶
-
classmethod
from_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 обрабатывает настраиваемые менеджеры и наследование моделей :
- Менеджеры из базовых классов всегда наследуются дочерним классом с использованием обычного порядка разрешения имен Python (имена в дочернем классе переопределяют все остальные; затем идут имена в первом родительском классе и т. Д.).
- Если менеджеры не объявлены для модели и / или ее родителей, Django автоматически создает
objects
менеджер. - Менеджер по умолчанию в классе - это либо тот, который выбран с помощью
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
копирования.