Как использовать для связанных полей в Django?
Я не могу понять это из документов. Мне это совершенно непонятно, более конкретно:
- это глобальная настройка? Итак, если я укажу этот атрибут на одном из менеджеров моделей, будет ли он использоваться глобально всеми классами моделей?
- Если это не глобальная настройка, то какие именно отношения будут затронуты?
- возможно ли иметь одного менеджера моделей для одного отношения и другого для другого отношения с тем же модель?
больше всего я был бы признателен за любые хорошие минимальные примеры использования, поскольку в документации нет этих afaik. Спасибо.
2 ответов
это глобальная настройка? Итак, если я укажу этот атрибут на одном из менеджеров моделей, будет ли он использоваться глобально всеми классами моделей?
если я понял, что вы подразумеваете под глобальным, ответ-нет. Он будет использоваться только для класса, если установлен диспетчер по умолчанию (первый менеджер, указанный в классе). Вы можете повторно использовать менеджер в нескольких моделях, и атрибут будет влиять только на те классы, которые он был менеджером по умолчанию для.
это то, что я думал, что пример поможет с пониманием. Позволяет использовать следующие модели элементов и профилей, связанные в отношениях "один к одному":
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}
здесь мы видим, что связь определяется путем хранения значения ID экземпляра члена. Когда мы ссылаемся на 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)
и затем мы добавили его в нашу модель участника в качестве менеджера по умолчанию (в реальной жизни это плохая идея™ как ряд утилит, таких как 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.
возможно ли иметь одного менеджера модели для одного отношения и другого для другого отношения с той же моделью?
нет. Хотя, в это обсуждение ошибки, упомянутой выше это как возможность в будущем.
короткий ответ: пока эта ошибка исправлено, 'use-for-related-fields'не работает в django, за исключением отношений один к одному, поэтому не беспокойтесь, если ваш вариант использования m2m или m2one, или вы будете разочарованы.