use_for_related_fields 如何在 Django 中工作?

Posted

技术标签:

【中文标题】use_for_related_fields 如何在 Django 中工作?【英文标题】:How does use_for_related_fields work in Django? 【发布时间】:2011-08-29 08:38:22 【问题描述】:

我无法从文档中理解这一点。我完全不清楚,更具体地说:

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

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

【问题讨论】:

【参考方案1】:

简短的回答是:在修复this bug 之前,'use-for-related-fields' 在 django 中不起作用,除了一对一的关系,所以不要'如果您的用例是 m2m 或 m2one,请不要担心,否则您会失望的。

【讨论】:

【参考方案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)

我们将创建几个成员,活跃的 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()

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

这里我们可以看到关系是通过存储成员实例的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

现在经理过滤掉了不活跃的用户,我们只能从数据库中检索 John 的成员资格:

>>> 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)

因此我们无法再从他的个人资料中获取 Phil 的会员记录:

>>> 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 属性仅用于一对一的关系。外键和多对多关系忽略它。这是 Django 跟踪器中的ticket #14891。

是否可以让一个模型经理负责一个关系,另一个负责同一模型的另一个关系?

没有。虽然,在 this discussion of the bug mentioned above 中,这在未来是一种可能性。

【讨论】:

直到现在才知道这样的答案可以像一部好的惊悚片一样写出来——最后有一个转折。我真的很感谢你详尽的解释。非常感谢。顺便说一句,Django 中有多少半生不熟的功能真的让我感到惊讶……好吧,至少我确切地知道为什么我不能使用这个功能。再次感谢。 我认为get_query_set 应该是get_queryset 这是很好的解释。谢谢布莱尔

以上是关于use_for_related_fields 如何在 Django 中工作?的主要内容,如果未能解决你的问题,请参考以下文章

来自 3 层嵌套的 Django ORM 总和价格

ByteDance字节跳动张一鸣:如何阅读如何了解自己如何与人沟通沟通如何安排时间如何正确的看待别人意见如何激励自己如何写作如何坚持锻炼身体如何耐心?...

Markdown公式用法大全

shell编程-如何定义函数如何调用函数如何调试shell

[精选] Mysql分表与分库如何拆分,如何设计,如何使用

四连问:前后端分离接口应该如何设计?如何保证安全?如何签名?如何防重?