Browse files

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()
  • Loading branch information...
1 parent dd1ab89 commit 349c12d3f5c015c2f8b36917da21230c2ac1acb4 @mjtamlyn mjtamlyn committed Oct 15, 2013
5 django/db/models/sql/
@@ -1712,7 +1712,10 @@ def add_select_related(self, fields):
certain related models (as opposed to all models, when
- 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):
74 docs/ref/models/querysets.txt
@@ -688,13 +688,12 @@ manager or a ``QuerySet`` and do further filtering on the result. After calling
-.. 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 has been prepopulated
# in the previous query.
b =
-``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,56 +730,21 @@ 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
- b = Book.objects.select_related().get(id=4)
+ b = Book.objects.select_related('person__city').get(id=4)
p = # Doesn't hit the database.
c = p.hometown # Doesn't hit the database.
b = Book.objects.get(id=4) # No select_related() in this example.
p = # 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,13 +753,27 @@ 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
``select_related`` on a ``QuerySet``, you can pass ``None`` as a parameter::
>>> 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')``.
6 docs/releases/1.7.txt
@@ -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
9 tests/select_related/
@@ -66,3 +66,12 @@ class Species(models.Model):
genus = models.ForeignKey(Genus)
def __str__(self):
+# and we'll invent a new thing so we have a model with two foreign keys
+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
11 tests/select_related/
@@ -2,7 +2,7 @@
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 @@ def test_depth_fields_fails(self):
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)

0 comments on commit 349c12d

Please sign in to comment.