diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index a50f1f0c15..b30133c0e5 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -442,28 +442,39 @@ class Query(object): self._select_aliases = aliases return result - def get_default_columns(self, with_aliases=False, col_aliases=None): + def get_default_columns(self, with_aliases=False, col_aliases=None, + start_alias=None, opts=None, as_pairs=False): """ Computes the default columns for selecting every field in the base model. Returns a list of strings, quoted appropriately for use in SQL - directly, as well as a set of aliases used in the select statement. + directly, as well as a set of aliases used in the select statement (if + 'as_pairs' is True, returns a list of (alias, col_name) pairs instead + of strings as the first component and None as the second component). """ result = [] - table_alias = self.tables[0] - root_pk = self.model._meta.pk.column + if opts is None: + opts = self.model._meta + if start_alias: + table_alias = start_alias + else: + table_alias = self.tables[0] + root_pk = opts.pk.column seen = {None: table_alias} qn = self.quote_name_unless_alias qn2 = self.connection.ops.quote_name aliases = set() - for field, model in self.model._meta.get_fields_with_model(): + for field, model in opts.get_fields_with_model(): try: alias = seen[model] except KeyError: alias = self.join((table_alias, model._meta.db_table, root_pk, model._meta.pk.column)) seen[model] = alias + if as_pairs: + result.append((alias, field.column)) + continue if with_aliases and field.column in col_aliases: c_alias = 'Col%d' % len(col_aliases) result.append('%s.%s AS %s' % (qn(alias), @@ -476,6 +487,8 @@ class Query(object): aliases.add(r) if with_aliases: col_aliases.add(field.column) + if as_pairs: + return result, None return result, aliases def get_from_clause(self): @@ -941,8 +954,8 @@ class Query(object): f.rel.get_related_field().column), exclusions=used, promote=promote) used.add(alias) - self.related_select_cols.extend([(alias, f2.column) - for f2 in f.rel.to._meta.fields]) + self.related_select_cols.extend(self.get_default_columns( + start_alias=alias, opts=f.rel.to._meta, as_pairs=True)[0]) self.related_select_fields.extend(f.rel.to._meta.fields) if restricted: next = requested.get(f.name, {}) diff --git a/tests/regressiontests/model_inheritance_select_related/__init__.py b/tests/regressiontests/model_inheritance_select_related/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regressiontests/model_inheritance_select_related/models.py b/tests/regressiontests/model_inheritance_select_related/models.py new file mode 100644 index 0000000000..4677e01787 --- /dev/null +++ b/tests/regressiontests/model_inheritance_select_related/models.py @@ -0,0 +1,48 @@ +""" +Regression tests for the interaction between model inheritance and +select_related(). +""" + +from django.db import models + +class Place(models.Model): + name = models.CharField(max_length=50) + + class Meta: + ordering = ('name',) + + def __unicode__(self): + return u"%s the place" % self.name + +class Restaurant(Place): + serves_sushi = models.BooleanField() + serves_steak = models.BooleanField() + + def __unicode__(self): + return u"%s the restaurant" % self.name + +class Person(models.Model): + name = models.CharField(max_length=50) + favorite_restaurant = models.ForeignKey(Restaurant) + + def __unicode__(self): + return self.name + +__test__ = {'API_TESTS':""" +Regression test for #7246 + +>>> r1 = Restaurant.objects.create(name="Nobu", serves_sushi=True, serves_steak=False) +>>> r2 = Restaurant.objects.create(name="Craft", serves_sushi=False, serves_steak=True) +>>> p1 = Person.objects.create(name="John", favorite_restaurant=r1) +>>> p2 = Person.objects.create(name="Jane", favorite_restaurant=r2) + +>>> Person.objects.order_by('name').select_related() +[, ] + +>>> Person.objects.order_by('name').select_related('favorite_restaurant').query.as_sql() +>>> jane = Person.objects.order_by('name').select_related('favorite_restaurant')[0] +>>> jane.favorite_restaurant.name +u'Craft' + +"""} +