use_for_related_fields如何在Django中工作?

时间:2021-03-13 20:30:09

I'm unable to grasp that from the docs. It's totally unclear to me, more specifically:

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

  • Is it a global setting? So if I specify this attribute it on one of the model managers, will it be used globally by all the model classes?
  • 这是一个全球性的环境吗?因此,如果我在其中一个模型管理器上指定此属性,它是否会被所有模型类全局使用?
  • If it's not a global setting then which relationships exactly will be affected?
  • 如果它不是全局设置那么哪些关系会受到影响?
  • Is it possible to have one model manager for one relationship and another one for another relationship with the same model?
  • 是否可以为一个关系设置一个模型管理器,为同一个模型设置另一个关系?

Most of all I would appreciate any good minimal example usages as the documentation lacks those afaik. Thanks.

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

2 个解决方案

#1


44  

Is it a global setting? So if I specify this attribute it on one of the model managers, will it be used globally by all the model classes?

If I have understood what you mean by global, the answer is no. It will only be used for a class if the default manager (the first manager specified in the class) has it set. You could re-use the manager in several models, and the attribute would only have an effect on those classes it was the default manager for.

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

This is something I thought an example would help with understanding. Lets use the following member and profile models, linked in a one-to-one relationship:

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

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)

We'll create a couple of members, the active John and the inactive Phil, and set up a profile for each of them:

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

How Django stores relationships

First, lets look at how Django stores a relationship. We'll take John's profile and look at its namespace:

首先,让我们看看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}

Here we can see the relationship is defined by storing the ID value of the member instance. When we reference the member attribute, Django uses a manager to retrieve the member details from the database and create the instance. Incidentally, this information is then cached in case we want to use it again:

在这里,我们可以看到通过存储成员实例的ID值来定义关系。当我们引用成员属性时,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}

Which manager to use

Unless told otherwise, Django uses a standard manager for this relationship lookup rather than any custom manager added to the model. For example, say we created the following manager to only return active members:

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

class ActiveManager(models.Manager):
    def get_query_set(self):
        return super(ActiveManager, self).get_query_set().filter(active=True)

And we then added it to our Member model as the default manager (in real life, this is a Bad Idea™ as a number of utilities, such as the dumpdata management command, exclusively use the default manager, and hence a default manager which filters out instances could lead to lost data or similar nasty side effects):

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

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

    objects = ActiveManager()

    def __unicode__(self):
        return self.name

Now that the manager filters out inactive users, we can only retrieve John's membership from the database:

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

>>> Member.objects.all()
[<Member: John>]

However, both profiles are available:

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

>>> Profile.objects.all()
[<Profile: 18>, <Profile: 35>]

And hence we can get to the inactive member through the profile, as the relationship lookup still uses a standard manager rather than our custom one:

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

>>> p = Profile.objects.get(id=2)
>>> p.member
<Member: Phil>

However, if We now set the use_for_related_fields attribute on our manager, this will tell Django it must use this manager for any relationship lookups:

但是,如果我们现在在我们的管理器上设置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)

Hence we can no longer get Phil's membership record from his profile:

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

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

Note that this only has an effect if the custom manager is the default manager for the model (i.e., it is the first manager defined). So, lets try using the standard manager as the default manager, and our custom one as a secondary manager:

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

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

The two managers work as expected when looking directly at the members:

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

>>> Member.objects.all()
[<Member: John>, <Member: Phil>]
>>> Member.active_members.all()
[<Member: John>]

And since the default manager can retrieve all objects, the relationship lookup succeeds too:

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

>>> Profile.objects.get(id=2)
>>> p.member
<Member: Phil>

The reality

Having made it this far, you now find out why I chose a one-to-one relationship for the example models. It turns out that in reality (and in contradiction of the documentation) the use_for_related_fields attribute is only used for one-to-one relationships. Foreign key and many-to-many relationships ignore it. This is ticket #14891 in the Django tracker.

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

Is it possible to have one model manager for one relationship and another one for another relationship with the same model?

No. Although, in this discussion of the bug mentioned above this came up as a possibility in the future.

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

#2


2  

The short answer is: until this bug is fixed, 'use-for-related-fields' doesn't work in django, except for one-to-one relationships, so don't bother if your use case is m2m or m2one, or you'll be disappointed.

简短的回答是:在修复此错误之前,'use-for-related-fields'在django中不起作用,除了一对一的关系,所以如果您的用例是m2m或m2one,请不要打扰,或者你会失望的。

#1


44  

Is it a global setting? So if I specify this attribute it on one of the model managers, will it be used globally by all the model classes?

If I have understood what you mean by global, the answer is no. It will only be used for a class if the default manager (the first manager specified in the class) has it set. You could re-use the manager in several models, and the attribute would only have an effect on those classes it was the default manager for.

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

This is something I thought an example would help with understanding. Lets use the following member and profile models, linked in a one-to-one relationship:

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

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)

We'll create a couple of members, the active John and the inactive Phil, and set up a profile for each of them:

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

How Django stores relationships

First, lets look at how Django stores a relationship. We'll take John's profile and look at its namespace:

首先,让我们看看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}

Here we can see the relationship is defined by storing the ID value of the member instance. When we reference the member attribute, Django uses a manager to retrieve the member details from the database and create the instance. Incidentally, this information is then cached in case we want to use it again:

在这里,我们可以看到通过存储成员实例的ID值来定义关系。当我们引用成员属性时,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}

Which manager to use

Unless told otherwise, Django uses a standard manager for this relationship lookup rather than any custom manager added to the model. For example, say we created the following manager to only return active members:

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

class ActiveManager(models.Manager):
    def get_query_set(self):
        return super(ActiveManager, self).get_query_set().filter(active=True)

And we then added it to our Member model as the default manager (in real life, this is a Bad Idea™ as a number of utilities, such as the dumpdata management command, exclusively use the default manager, and hence a default manager which filters out instances could lead to lost data or similar nasty side effects):

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

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

    objects = ActiveManager()

    def __unicode__(self):
        return self.name

Now that the manager filters out inactive users, we can only retrieve John's membership from the database:

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

>>> Member.objects.all()
[<Member: John>]

However, both profiles are available:

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

>>> Profile.objects.all()
[<Profile: 18>, <Profile: 35>]

And hence we can get to the inactive member through the profile, as the relationship lookup still uses a standard manager rather than our custom one:

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

>>> p = Profile.objects.get(id=2)
>>> p.member
<Member: Phil>

However, if We now set the use_for_related_fields attribute on our manager, this will tell Django it must use this manager for any relationship lookups:

但是,如果我们现在在我们的管理器上设置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)

Hence we can no longer get Phil's membership record from his profile:

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

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

Note that this only has an effect if the custom manager is the default manager for the model (i.e., it is the first manager defined). So, lets try using the standard manager as the default manager, and our custom one as a secondary manager:

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

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

The two managers work as expected when looking directly at the members:

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

>>> Member.objects.all()
[<Member: John>, <Member: Phil>]
>>> Member.active_members.all()
[<Member: John>]

And since the default manager can retrieve all objects, the relationship lookup succeeds too:

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

>>> Profile.objects.get(id=2)
>>> p.member
<Member: Phil>

The reality

Having made it this far, you now find out why I chose a one-to-one relationship for the example models. It turns out that in reality (and in contradiction of the documentation) the use_for_related_fields attribute is only used for one-to-one relationships. Foreign key and many-to-many relationships ignore it. This is ticket #14891 in the Django tracker.

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

Is it possible to have one model manager for one relationship and another one for another relationship with the same model?

No. Although, in this discussion of the bug mentioned above this came up as a possibility in the future.

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

#2


2  

The short answer is: until this bug is fixed, 'use-for-related-fields' doesn't work in django, except for one-to-one relationships, so don't bother if your use case is m2m or m2one, or you'll be disappointed.

简短的回答是:在修复此错误之前,'use-for-related-fields'在django中不起作用,除了一对一的关系,所以如果您的用例是m2m或m2one,请不要打扰,或者你会失望的。