Это глобальная настройка? Поэтому, если я укажу этот атрибут в одном из менеджеров моделей, будет ли он использоваться глобально всеми классами моделей?
Если я понял, что вы подразумеваете под глобальным, ответ - нет. Он будет использоваться для класса, только если он установлен менеджером по умолчанию (первый менеджер, указанный в классе). Вы можете повторно использовать менеджер в нескольких моделях, и атрибут будет влиять только на те классы, для которых он был менеджером по умолчанию.
Это то, что я думал, пример поможет понять. Позволяет использовать следующие модели членов и профилей, связанные один-к-одному:
from django.db import models
class Member(models.Model):
name = models.CharField(max_length=100)
active = models.BooleanField()
def __unicode__(self):
return self.name
class Profile(models.Model):
member = models.OneToOneField(Member)
age = models.PositiveIntegerField()
def __unicode__(self):
return str(self.age)
Мы создадим пару членов, активного Джона и неактивного Фила, и настроим профиль для каждого из них:
>>> m1 = Member(name='John', active=True)
>>> m1.save()
>>> p1 = Profile(member=m1, age=18)
>>> p1.save()
>>> m2 = Member(name='Phil', active=False)
>>> m2.save()
>>> p2 = Profile(member=m2, age=35)
>>> p2.save()
Как Django хранит отношения
Сначала давайте посмотрим, как Django хранит отношения. Мы возьмем профиль Джона и посмотрим на его пространство имен:
>>> p = Profile.objects.get(id=1)
>>> p.__dict__
{'age': 18, '_state': <django.db.models.base.ModelState object at 0x95d054c>, 'id': 1, 'member_id': 1}
Здесь мы видим, что отношение определяется сохранением значения идентификатора экземпляра члена. Когда мы ссылаемся на атрибут member
, Django использует менеджера для получения сведений о члене из базы данных и создания экземпляра. Кстати, эта информация затем кэшируется на случай, если мы захотим использовать ее снова:
>>> p.member
<Member: John>
>>> p.__dict__
{'age': 18, '_member_cache': <Member: John>, '_state': <django.db.models.base.ModelState object at 0x95d054c>, 'id': 1, 'member_id': 1}
Какой менеджер использовать
Если не указано иное, Django использует стандартный менеджер для поиска этих отношений, а не какой-либо пользовательский менеджер, добавленный в модель. Например, скажем, мы создали следующий менеджер, чтобы возвращать только активных участников:
class ActiveManager(models.Manager):
def get_query_set(self):
return super(ActiveManager, self).get_query_set().filter(active=True)
И затем мы добавили его в нашу модель Member в качестве менеджера по умолчанию (в реальной жизни это плохая идея и торговля; в качестве ряда утилит, таких как команда управления dumpdata
, используется исключительно менеджер по умолчанию, и, следовательно менеджер по умолчанию, который отфильтровывает экземпляры, может привести к потере данных или аналогичным неприятным побочным эффектам):
class Member(models.Model):
name = models.CharField(max_length=100)
active = models.BooleanField()
objects = ActiveManager()
def __unicode__(self):
return self.name
Теперь, когда менеджер отфильтровывает неактивных пользователей, мы можем получить членство Джона только из базы данных:
>>> Member.objects.all()
[<Member: John>]
Однако доступны оба профиля:
>>> Profile.objects.all()
[<Profile: 18>, <Profile: 35>]
И, следовательно, мы можем получить доступ к неактивному члену через профиль, так как для поиска отношений по-прежнему используется стандартный менеджер, а не наш пользовательский:
>>> p = Profile.objects.get(id=2)
>>> p.member
<Member: Phil>
Однако, если мы теперь установим атрибут use_for_related_fields
для нашего менеджера, это скажет Django, что он должен использовать этот менеджер для любых поисков отношений:
class ActiveManager(models.Manager):
use_for_related_fields = True
def get_query_set(self):
return super(ActiveManager, self).get_query_set().filter(active=True)
Следовательно, мы больше не можем получить учетную запись Фила из его профиля:
>>> p = Profile.objects.get(id=2)
>>> p.member
---------------------------------------------------------------------------
DoesNotExist Traceback (most recent call last)
/home/blair/<ipython console> in <module>()
/usr/lib/pymodules/python2.6/django/db/models/fields/related.pyc in __get__(self, instance, instance_type)
298 db = router.db_for_read(self.field.rel.to, instance=instance)
299 if getattr(rel_mgr, 'use_for_related_fields', False):
--> 300 rel_obj = rel_mgr.using(db).get(**params)
301 else:
302 rel_obj = QuerySet(self.field.rel.to).using(db).get(**params)
/usr/lib/pymodules/python2.6/django/db/models/query.pyc in get(self, *args, **kwargs)
339 if not num:
340 raise self.model.DoesNotExist("%s matching query does not exist."
--> 341 % self.model._meta.object_name)
342 raise self.model.MultipleObjectsReturned("get() returned more than one %s -- it returned %s! Lookup parameters were %s"
343 % (self.model._meta.object_name, num, kwargs))
DoesNotExist: Member matching query does not exist.
Обратите внимание, что это действует только в том случае, если пользовательский менеджер является менеджером по умолчанию для модели (т. Е. Это первый определенный менеджер). Итак, давайте попробуем использовать стандартный менеджер в качестве менеджера по умолчанию и наш собственный менеджер в качестве вторичного менеджера:
class Member(models.Model):
name = models.CharField(max_length=100)
active = models.BooleanField()
objects = models.Manager()
active_members = ActiveManager()
def __unicode__(self):
return self.name
Два менеджера работают, как и ожидалось, глядя прямо на участников:
>>> Member.objects.all()
[<Member: John>, <Member: Phil>]
>>> Member.active_members.all()
[<Member: John>]
И поскольку менеджер по умолчанию может извлекать все объекты, поиск отношений также выполняется успешно:
>>> Profile.objects.get(id=2)
>>> p.member
<Member: Phil>
Реальность
Пройдя так далеко, вы теперь узнаете, почему я выбрал отношение один-к-одному для примеров моделей. Оказывается, что в действительности (и в противоречие с документацией) атрибут use_for_related_fields
используется только для отношений один-к-одному. Внешний ключ и отношения многие ко многим игнорируют его. Это билет # 14891 в трекере Django.
Возможно ли иметь одного менеджера модели для одного отношения и другого для другого отношения с той же моделью?
Нет. Хотя в это обсуждение упомянутой выше ошибки в будущем это стало возможным.