Как использовать для связанных полей в 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, или вы будете разочарованы.