Fixed #16855 -- select_related() chains as expected.
select_related('foo').select_related('bar') is now equivalent to select_related('foo', 'bar'). Also reworded docs to recommend select_related(*fields) over select_related()
This commit is contained in:
parent
dd1ab8982b
commit
349c12d3f5
|
@ -1712,7 +1712,10 @@ class Query(object):
|
|||
certain related models (as opposed to all models, when
|
||||
self.select_related=True).
|
||||
"""
|
||||
field_dict = {}
|
||||
if isinstance(self.select_related, bool):
|
||||
field_dict = {}
|
||||
else:
|
||||
field_dict = self.select_related
|
||||
for field in fields:
|
||||
d = field_dict
|
||||
for part in field.split(LOOKUP_SEP):
|
||||
|
|
|
@ -688,13 +688,12 @@ manager or a ``QuerySet`` and do further filtering on the result. After calling
|
|||
select_related
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
.. method:: select_related()
|
||||
.. method:: select_related(*fields)
|
||||
|
||||
Returns a ``QuerySet`` that will automatically "follow" foreign-key
|
||||
relationships, selecting that additional related-object data when it executes
|
||||
its query. This is a performance booster which results in (sometimes much)
|
||||
larger queries but means later use of foreign-key relationships won't require
|
||||
database queries.
|
||||
Returns a ``QuerySet`` that will "follow" foreign-key relationships, selecting
|
||||
additional related-object data when it executes its query. This is a
|
||||
performance booster which results in a single more complex query but means
|
||||
later use of foreign-key relationships won't require database queries.
|
||||
|
||||
The following examples illustrate the difference between plain lookups and
|
||||
``select_related()`` lookups. Here's standard lookup::
|
||||
|
@ -708,13 +707,13 @@ The following examples illustrate the difference between plain lookups and
|
|||
And here's ``select_related`` lookup::
|
||||
|
||||
# Hits the database.
|
||||
e = Entry.objects.select_related().get(id=5)
|
||||
e = Entry.objects.select_related('blog').get(id=5)
|
||||
|
||||
# Doesn't hit the database, because e.blog has been prepopulated
|
||||
# in the previous query.
|
||||
b = e.blog
|
||||
|
||||
``select_related()`` follows foreign keys as far as possible. If you have the
|
||||
You can follow foreign keys in a similar way to querying them. If you have the
|
||||
following models::
|
||||
|
||||
from django.db import models
|
||||
|
@ -731,10 +730,11 @@ following models::
|
|||
# ...
|
||||
author = models.ForeignKey(Person)
|
||||
|
||||
...then a call to ``Book.objects.select_related().get(id=4)`` will cache the
|
||||
related ``Person`` *and* the related ``City``::
|
||||
...then a call to ``Book.objects.select_related('person',
|
||||
'person__city').get(id=4)`` will cache the related ``Person`` *and* the related
|
||||
``City``::
|
||||
|
||||
b = Book.objects.select_related().get(id=4)
|
||||
b = Book.objects.select_related('person__city').get(id=4)
|
||||
p = b.author # Doesn't hit the database.
|
||||
c = p.hometown # Doesn't hit the database.
|
||||
|
||||
|
@ -742,45 +742,9 @@ related ``Person`` *and* the related ``City``::
|
|||
p = b.author # Hits the database.
|
||||
c = p.hometown # Hits the database.
|
||||
|
||||
Note that, by default, ``select_related()`` does not follow foreign keys that
|
||||
have ``null=True``.
|
||||
|
||||
Usually, using ``select_related()`` can vastly improve performance because your
|
||||
app can avoid many database calls. However, there are times you are only
|
||||
interested in specific related models, or have deeply nested sets of
|
||||
relationships, and in these cases ``select_related()`` can be optimized by
|
||||
explicitly passing the related field names you are interested in. Only
|
||||
the specified relations will be followed.
|
||||
|
||||
You can even do this for models that are more than one relation away by
|
||||
separating the field names with double underscores, just as for filters. For
|
||||
example, if you have this model::
|
||||
|
||||
class Room(models.Model):
|
||||
# ...
|
||||
building = models.ForeignKey(...)
|
||||
|
||||
class Group(models.Model):
|
||||
# ...
|
||||
teacher = models.ForeignKey(...)
|
||||
room = models.ForeignKey(Room)
|
||||
subject = models.ForeignKey(...)
|
||||
|
||||
...and you only needed to work with the ``room`` and ``subject`` attributes,
|
||||
you could write this::
|
||||
|
||||
g = Group.objects.select_related('room', 'subject')
|
||||
|
||||
This is also valid::
|
||||
|
||||
g = Group.objects.select_related('room__building', 'subject')
|
||||
|
||||
...and would also pull in the ``building`` relation.
|
||||
|
||||
You can refer to any :class:`~django.db.models.ForeignKey` or
|
||||
:class:`~django.db.models.OneToOneField` relation in the list of fields
|
||||
passed to ``select_related()``. This includes foreign keys that have
|
||||
``null=True`` (which are omitted in a no-parameter ``select_related()`` call).
|
||||
passed to ``select_related()``.
|
||||
|
||||
You can also refer to the reverse direction of a
|
||||
:class:`~django.db.models.OneToOneField` in the list of fields passed to
|
||||
|
@ -789,6 +753,13 @@ You can also refer to the reverse direction of a
|
|||
is defined. Instead of specifying the field name, use the :attr:`related_name
|
||||
<django.db.models.ForeignKey.related_name>` for the field on the related object.
|
||||
|
||||
There may be some situations where you wish to call ``select_related()`` with a
|
||||
lot of related objects, or where you don't know all of the relations. In these
|
||||
cases it is possible to call ``select_related()`` with no arguments. This will
|
||||
follow all non-null foreign keys it can find - nullable foreign keys must be
|
||||
specified. This is not recommended in most cases as it is likely to make the
|
||||
underlying query more complex, and return more data, than is actually needed.
|
||||
|
||||
.. versionadded:: 1.6
|
||||
|
||||
If you need to clear the list of related fields added by past calls of
|
||||
|
@ -796,6 +767,13 @@ If you need to clear the list of related fields added by past calls of
|
|||
|
||||
>>> without_relations = queryset.select_related(None)
|
||||
|
||||
.. versionchanged:: 1.7
|
||||
|
||||
Chaining ``select_related`` calls now works in a similar way to other methods -
|
||||
that is that ``select_related('foo', 'bar')`` is equivalent to
|
||||
``select_related('foo').select_related('bar')``. Previously the latter would
|
||||
have been equivalent to ``select_related('bar')``.
|
||||
|
||||
prefetch_related
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -533,6 +533,12 @@ Miscellaneous
|
|||
you relied on the default field ordering while having fields defined on both
|
||||
the current class *and* on a parent ``Form``.
|
||||
|
||||
* :meth:`~django.db.models.query.QuerySet.select_related` now chains in the
|
||||
same way as other similar calls like ``prefetch_related``. That is,
|
||||
``select_related('foo', 'bar')`` is equivalent to
|
||||
``select_related('foo').select_related('bar')``. Previously the latter would
|
||||
have been equivalent to ``select_related('bar')``.
|
||||
|
||||
Features deprecated in 1.7
|
||||
==========================
|
||||
|
||||
|
|
|
@ -66,3 +66,12 @@ class Species(models.Model):
|
|||
genus = models.ForeignKey(Genus)
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
# and we'll invent a new thing so we have a model with two foreign keys
|
||||
@python_2_unicode_compatible
|
||||
class HybridSpecies(models.Model):
|
||||
name = models.CharField(max_length=50)
|
||||
parent_1 = models.ForeignKey(Species, related_name='child_1')
|
||||
parent_2 = models.ForeignKey(Species, related_name='child_2')
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
|
|
@ -2,7 +2,7 @@ from __future__ import unicode_literals
|
|||
|
||||
from django.test import TestCase
|
||||
|
||||
from .models import Domain, Kingdom, Phylum, Klass, Order, Family, Genus, Species
|
||||
from .models import Domain, Kingdom, Phylum, Klass, Order, Family, Genus, Species, HybridSpecies
|
||||
|
||||
|
||||
class SelectRelatedTests(TestCase):
|
||||
|
@ -144,3 +144,12 @@ class SelectRelatedTests(TestCase):
|
|||
def test_none_clears_list(self):
|
||||
queryset = Species.objects.select_related('genus').select_related(None)
|
||||
self.assertEqual(queryset.query.select_related, False)
|
||||
|
||||
def test_chaining(self):
|
||||
parent_1, parent_2 = Species.objects.all()[:2]
|
||||
HybridSpecies.objects.create(name='hybrid', parent_1=parent_1, parent_2=parent_2)
|
||||
queryset = HybridSpecies.objects.select_related('parent_1').select_related('parent_2')
|
||||
with self.assertNumQueries(1):
|
||||
obj = queryset[0]
|
||||
self.assertEquals(obj.parent_1, parent_1)
|
||||
self.assertEquals(obj.parent_2, parent_2)
|
||||
|
|
Loading…
Reference in New Issue