Менеджеры ¶
-
класс
Manager
¶
Обработчик (объект Manager
) - это интерфейс, через который операции запросов к базе данных становятся доступными для моделей Django. В Manager
приложении Django есть как минимум один для каждой модели.
Работа классов Manager
описана в разделе Создание запросов ; в этом документе конкретно обсуждаются параметры шаблона, которые настраивают поведение менеджера.
Имена менеджеров ¶
По умолчанию Django добавляет именованный обработчик objects
к каждому классу модели Django. Однако, если вы хотите использовать objects
в качестве имени поля или хотите присвоить обработчику другое имя objects
, вы можете переименовать его на уровне модели. Чтобы переименовать обработчик для данного класса, определите атрибут класса типа models.Manager()
в модели. Например :
from django.db import models
class Person(models.Model):
#...
people = models.Manager()
Использование этого шаблона примера Person.objects
вызовет исключение AttributeError
, но Person.people.all()
фактически перечислит все объекты Person
.
Пользовательские менеджеры ¶
Вы можете использовать собственный обработчик в конкретной модели, расширив Manager
базовый класс и создав свой собственный экземпляр Manager
в своей модели.
Настроить обработчик нужно по двум причинам: добавить к нему дополнительные методы или изменить QuerySet
исходный объект , возвращаемый обработчиком.
Добавление дополнительных методов-обработчиков ¶
Добавление дополнительных методов-обработчиков является предпочтительным способом добавления к моделям функциональности на уровне таблицы (для функциональности на уровне строк, то есть функций, которые действуют на один экземпляр объекта модели используйте методы модели , а не методы настраиваемого обработчика).
Пользовательский метод обработчика может возвращать все, что вы хотите, ему не обязательно возвращать объект QuerySet
.
Например, этот метод настраиваемого обработчика предлагает метод, with_counts()
который возвращает список всех объектов OpinionPoll
, каждый из которых получает дополнительный атрибут, num_responses
который является результатом агрегированного запроса:
from django.db import models
class PollManager(models.Manager):
def with_counts(self):
from django.db import connection
with connection.cursor() as cursor:
cursor.execute("""
SELECT p.id, p.question, p.poll_date, COUNT(*)
FROM polls_opinionpoll p, polls_response r
WHERE p.id = r.poll_id
GROUP BY p.id, p.question, p.poll_date
ORDER BY p.poll_date DESC""")
result_list = []
for row in cursor.fetchall():
p = self.model(id=row[0], question=row[1], poll_date=row[2])
p.num_responses = row[3]
result_list.append(p)
return result_list
class OpinionPoll(models.Model):
question = models.CharField(max_length=200)
poll_date = models.DateField()
objects = PollManager()
class Response(models.Model):
poll = models.ForeignKey(OpinionPoll, on_delete=models.CASCADE)
person_name = models.CharField(max_length=50)
response = models.TextField()
В этом примере вы должны написать, OpinionPoll.objects.with_counts()
чтобы получить список объектов OpinionPoll
с атрибутом num_responses
.
Еще одна вещь, на которую следует обратить внимание в этом примере, заключается в том, что методы-обработчики имеют доступ self.model
для получения класса модели, к которому они привязаны.
Смена QuerySet
инициалов менеджера ¶
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()
возвращает все книги в базе данных.
Вы можете переопределить QuerySet
базу обработчика, переопределив метод Manager.get_queryset()
. get_queryset()
должен вернуть объект QuerySet
с необходимыми свойствами.
Например, в следующей модели есть два обработчика, один из которых возвращает все объекты, а другой - только книги Роальда Даля:
# 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()
Этот пример также выдвинул на первый план еще один интересный прием: использование нескольких менеджеров в одной модели. Вы можете связать с шаблоном столько экземпляров менеджера, сколько хотите. Это неповторяющийся способ определения часто используемых «фильтров» в моделях.
Например :
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 интерпретирует первый обработчик, определенный в классе, как обработчик по умолчанию, и некоторые части Django (включая dumpdata
) используют этот обработчик исключительно для рассматриваемой модели. В результате желательно тщательно выбирать обработчик по умолчанию, чтобы избежать ситуации, когда перегрузка get_queryset()
приводит к невозможности получить объекты, с которыми вам нужно работать.
Вы можете определить собственный обработчик по умолчанию, используя Meta.default_manager_name
.
Например, если вы пишете код, который должен обрабатывать неизвестную модель в стороннем приложении, которое реализует универсальное представление, используйте этот обработчик (или _base_manager
), а не предполагайте, что у модели есть обработчик objects
.
Базовые менеджеры ¶
-
Model.
_base_manager
¶
Никогда не фильтруйте результаты в этом типе подкласса обработчика ¶
Этот менеджер используется для доступа к связанным объектам из другой модели. В этих ситуациях Django должен иметь возможность видеть все объекты модели, которые он извлекает, чтобы можно было получить все , на что есть ссылки.
Следовательно, не следует переопределять get_queryset()
фильтрацию каких-либо строк. Если вы это сделаете, Django вернет неполные результаты.
Вызов кастомного метода QuerySet
из менеджера ¶
Хотя большинство методов QuerySet
стандарта доступны напрямую из a 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
.
Создание объекта ¶Manager
методамиQuerySet
Вместо вышеуказанного подхода, который требует дублирования методов для обоих объектов QuerySet
и Manager
, QuerySet.as_manager()
можно использовать для создания экземпляра Manager
с копией методов QuerySet
настраиваемого объекта :
class Person(models.Model):
...
people = PersonQuerySet.as_manager()
Экземпляр Manager
created by 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 ) ¶
Для более продвинутого использования может быть желательно иметь как Manager
пользовательский, так и 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
абстрактную модель.
Детали реализации ¶
Какие бы функциональные возможности ни были добавлены в настраиваемый менеджер, всегда должна быть возможность сделать легкую («неглубокую») копию одного из его экземпляров; то есть следующий код должен работать:
>>> import copy
>>> manager = MyManager()
>>> my_copy = copy.copy(manager)
Django делает легкие копии объектов-обработчиков во время определенных запросов; если ваш обработчик не может быть скопирован, эти запросы не будут выполнены.
Для большинства кастомных менеджеров это не проблема. Если вы просто добавляете простые методы в свой класс Manager
, вы вряд ли предотвратите непреднамеренное копирование обработчиком. Однако, если вы переопределяете __getattr__
или другие частные методы объекта, Manager
которые управляют состоянием объекта, убедитесь, что вы не изменяете возможность копирования вашего обработчика.