I'm unable to grasp that from the docs. It's totally unclear to me, more specifically:
Most of all I would appreciate any good minimal example usages as the documentation lacks those afaik. Thanks.
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:
>>> 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()
First, lets look at how Django stores a relationship. We'll take John's profile and look at its namespace:
>>> 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:
>>> 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}
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:
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):
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:
>>> 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:
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>
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.
No. Although, in this discussion of the bug mentioned above this came up as a possibility in the future.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With