use_for_related_fields如何在Django中运行?

jul*_*icz 17 python django django-models foreign-key-relationship django-managers

我无法从文档中了解到这一点.对我来说完全不清楚,更具体地说:

  • 这是一个全球性的环境吗?因此,如果我在其中一个模型管理器上指定此属性,它是否会被所有模型类全局使用?
  • 如果它不是全局设置,那么哪些关系会受到影响?
  • 是否可以为一个关系设置一个模型管理器,为同一个模型设置另一个关系?

最重要的是,我会感谢任何好的最小示例用法,因为文档缺乏那些afaik.谢谢.

Bla*_*air 49

这是一个全球性的环境吗?因此,如果我在其中一个模型管理器上指定此属性,它是否会被所有模型类全局使用?

如果我理解了全球的意思,那么答案就是否定的.如果默认管理器(类中指定的第一个管理器)设置了它,它将仅用于类.您可以在多个模型中重用经理,该属性只对那些作为默认管理器的类产生影响.

这是我认为一个例子有助于理解的东西.让我们使用以一对一关系链接的以下成员和个人资料模型:

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)
Run Code Online (Sandbox Code Playgroud)

我们将创建几个成员,活跃的John和非活动的Phil,并为每个成员设置一个配置文件:

>>> 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()
Run Code Online (Sandbox Code Playgroud)

Django如何存储关系

首先,让我们看看Django如何存储关系.我们将采用John的配置文件并查看其命名空间:

>>> p = Profile.objects.get(id=1)
>>> p.__dict__
{'age': 18, '_state': <django.db.models.base.ModelState object at 0x95d054c>, 'id': 1, 'member_id': 1}
Run Code Online (Sandbox Code Playgroud)

在这里,我们可以看到通过存储成员实例的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}
Run Code Online (Sandbox Code Playgroud)

使用哪个经理

除非另有说明,否则Django使用标准管理器进行此关系查找,而不是添加到模型中的任何自定义管理器.例如,假设我们创建了以下管理器以仅返回活动成员:

class ActiveManager(models.Manager):
    def get_query_set(self):
        return super(ActiveManager, self).get_query_set().filter(active=True)
Run Code Online (Sandbox Code Playgroud)

然后我们将它作为默认管理器添加到我们的成员模型中(在现实生活中,这是一个Bad Idea™,因为许多实用程序,例如dumpdata管理命令,专门使用默认管理器,因此是一个过滤掉的默认管理器实例可能导致数据丢失或类似的令人讨厌的副作用):

class Member(models.Model):
    name = models.CharField(max_length=100)
    active = models.BooleanField()

    objects = ActiveManager()

    def __unicode__(self):
        return self.name
Run Code Online (Sandbox Code Playgroud)

既然管理器过滤掉了非活动用户,我们只能从数据库中检索John的成员资格:

>>> Member.objects.all()
[<Member: John>]
Run Code Online (Sandbox Code Playgroud)

但是,这两个配置文件都可用:

>>> Profile.objects.all()
[<Profile: 18>, <Profile: 35>]
Run Code Online (Sandbox Code Playgroud)

因此,我们可以通过配置文件访问非活动成员,因为关系查找仍然使用标准管理器而不是我们的自定义管理器:

>>> p = Profile.objects.get(id=2)
>>> p.member
<Member: Phil>
Run Code Online (Sandbox Code Playgroud)

但是,如果我们现在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)
Run Code Online (Sandbox Code Playgroud)

因此,我们再也无法从他的个人资料中获得菲尔的会员记录:

>>> 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.
Run Code Online (Sandbox Code Playgroud)

请注意,如果自定义管理器是模型的默认管理器(即,它是第一个定义的管理器),这只会产生影响.因此,让我们尝试使用标准管理器作为默认管理器,并将我们的自定义管理器用作辅助管理器:

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
Run Code Online (Sandbox Code Playgroud)

直接观察成员时,两位经理按预期工作:

>>> Member.objects.all()
[<Member: John>, <Member: Phil>]
>>> Member.active_members.all()
[<Member: John>]
Run Code Online (Sandbox Code Playgroud)

由于默认管理器可以检索所有对象,因此关系查找也会成功:

>>> Profile.objects.get(id=2)
>>> p.member
<Member: Phil>
Run Code Online (Sandbox Code Playgroud)

现实

已经做到这一点,你现在找出为什么我为示例模型选择一对一的关系.事实证明,在现实中(与文档相矛盾),该use_for_related_fields属性仅用于一对一关系.外键和多对多关系会忽略它.这是Django跟踪器中的#14891票.

是否可以为一个关系设置一个模型管理器,为同一个模型设置另一个关系?

不.虽然,在上面提到的这个bug的讨论中,这种可能性在未来出现了.

  • 直到现在才知道一个SO的答案可以写成一部好的惊悚片 - 最后有一个扭曲.我非常感谢您的详尽解释.非常感谢.另外一点,我真的很惊讶Django中有多少一半的烘焙功能......至少我知道为什么我不能使用这个功能.再次感谢. (4认同)