mirror of https://github.com/django/django.git
Fixed #9926 -- Fixes for some select_related() situations.
Using select_related(...) across a nullable relation to a multi-table model inheritance situation no longer excludes results. Thanks to AdamG for a test demonstrating part of the problem. git-svn-id: http://code.djangoproject.com/svn/django/trunk@10136 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
c5aa6fa710
commit
fd46f673bd
|
@ -770,6 +770,7 @@ class BaseQuery(object):
|
||||||
continue
|
continue
|
||||||
if as_pairs:
|
if as_pairs:
|
||||||
result.append((alias, field.column))
|
result.append((alias, field.column))
|
||||||
|
aliases.add(alias)
|
||||||
continue
|
continue
|
||||||
if with_aliases and field.column in col_aliases:
|
if with_aliases and field.column in col_aliases:
|
||||||
c_alias = 'Col%d' % len(col_aliases)
|
c_alias = 'Col%d' % len(col_aliases)
|
||||||
|
@ -784,7 +785,7 @@ class BaseQuery(object):
|
||||||
if with_aliases:
|
if with_aliases:
|
||||||
col_aliases.add(field.column)
|
col_aliases.add(field.column)
|
||||||
if as_pairs:
|
if as_pairs:
|
||||||
return result, None
|
return result, aliases
|
||||||
return result, aliases
|
return result, aliases
|
||||||
|
|
||||||
def get_from_clause(self):
|
def get_from_clause(self):
|
||||||
|
@ -1342,6 +1343,7 @@ class BaseQuery(object):
|
||||||
if model:
|
if model:
|
||||||
int_opts = opts
|
int_opts = opts
|
||||||
alias = root_alias
|
alias = root_alias
|
||||||
|
alias_chain = []
|
||||||
for int_model in opts.get_base_chain(model):
|
for int_model in opts.get_base_chain(model):
|
||||||
lhs_col = int_opts.parents[int_model].column
|
lhs_col = int_opts.parents[int_model].column
|
||||||
dedupe = lhs_col in opts.duplicate_targets
|
dedupe = lhs_col in opts.duplicate_targets
|
||||||
|
@ -1353,8 +1355,11 @@ class BaseQuery(object):
|
||||||
alias = self.join((alias, int_opts.db_table, lhs_col,
|
alias = self.join((alias, int_opts.db_table, lhs_col,
|
||||||
int_opts.pk.column), exclusions=used,
|
int_opts.pk.column), exclusions=used,
|
||||||
promote=promote)
|
promote=promote)
|
||||||
|
alias_chain.append(alias)
|
||||||
for (dupe_opts, dupe_col) in dupe_set:
|
for (dupe_opts, dupe_col) in dupe_set:
|
||||||
self.update_dupe_avoidance(dupe_opts, dupe_col, alias)
|
self.update_dupe_avoidance(dupe_opts, dupe_col, alias)
|
||||||
|
if self.alias_map[root_alias][JOIN_TYPE] == self.LOUTER:
|
||||||
|
self.promote_alias_chain(alias_chain, True)
|
||||||
else:
|
else:
|
||||||
alias = root_alias
|
alias = root_alias
|
||||||
|
|
||||||
|
@ -1368,8 +1373,11 @@ class BaseQuery(object):
|
||||||
f.rel.get_related_field().column),
|
f.rel.get_related_field().column),
|
||||||
exclusions=used.union(avoid), promote=promote)
|
exclusions=used.union(avoid), promote=promote)
|
||||||
used.add(alias)
|
used.add(alias)
|
||||||
self.related_select_cols.extend(self.get_default_columns(
|
columns, aliases = self.get_default_columns(start_alias=alias,
|
||||||
start_alias=alias, opts=f.rel.to._meta, as_pairs=True)[0])
|
opts=f.rel.to._meta, as_pairs=True)
|
||||||
|
self.related_select_cols.extend(columns)
|
||||||
|
if self.alias_map[alias][JOIN_TYPE] == self.LOUTER:
|
||||||
|
self.promote_alias_chain(aliases, True)
|
||||||
self.related_select_fields.extend(f.rel.to._meta.fields)
|
self.related_select_fields.extend(f.rel.to._meta.fields)
|
||||||
if restricted:
|
if restricted:
|
||||||
next = requested.get(f.name, {})
|
next = requested.get(f.name, {})
|
||||||
|
|
|
@ -16,10 +16,17 @@ try:
|
||||||
except NameError:
|
except NameError:
|
||||||
from django.utils.itercompat import sorted
|
from django.utils.itercompat import sorted
|
||||||
|
|
||||||
|
class DumbCategory(models.Model):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class NamedCategory(DumbCategory):
|
||||||
|
name = models.CharField(max_length=10)
|
||||||
|
|
||||||
class Tag(models.Model):
|
class Tag(models.Model):
|
||||||
name = models.CharField(max_length=10)
|
name = models.CharField(max_length=10)
|
||||||
parent = models.ForeignKey('self', blank=True, null=True,
|
parent = models.ForeignKey('self', blank=True, null=True,
|
||||||
related_name='children')
|
related_name='children')
|
||||||
|
category = models.ForeignKey(NamedCategory, null=True, default=None)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['name']
|
ordering = ['name']
|
||||||
|
@ -266,8 +273,9 @@ class Plaything(models.Model):
|
||||||
|
|
||||||
|
|
||||||
__test__ = {'API_TESTS':"""
|
__test__ = {'API_TESTS':"""
|
||||||
>>> t1 = Tag.objects.create(name='t1')
|
>>> generic = NamedCategory.objects.create(name="Generic")
|
||||||
>>> t2 = Tag.objects.create(name='t2', parent=t1)
|
>>> t1 = Tag.objects.create(name='t1', category=generic)
|
||||||
|
>>> t2 = Tag.objects.create(name='t2', parent=t1, category=generic)
|
||||||
>>> t3 = Tag.objects.create(name='t3', parent=t1)
|
>>> t3 = Tag.objects.create(name='t3', parent=t1)
|
||||||
>>> t4 = Tag.objects.create(name='t4', parent=t3)
|
>>> t4 = Tag.objects.create(name='t4', parent=t3)
|
||||||
>>> t5 = Tag.objects.create(name='t5', parent=t3)
|
>>> t5 = Tag.objects.create(name='t5', parent=t3)
|
||||||
|
@ -726,6 +734,12 @@ Bug #6981
|
||||||
>>> Tag.objects.select_related('parent').order_by('name')
|
>>> Tag.objects.select_related('parent').order_by('name')
|
||||||
[<Tag: t1>, <Tag: t2>, <Tag: t3>, <Tag: t4>, <Tag: t5>]
|
[<Tag: t1>, <Tag: t2>, <Tag: t3>, <Tag: t4>, <Tag: t5>]
|
||||||
|
|
||||||
|
Bug #9926
|
||||||
|
>>> Tag.objects.select_related("parent", "category").order_by('name')
|
||||||
|
[<Tag: t1>, <Tag: t2>, <Tag: t3>, <Tag: t4>, <Tag: t5>]
|
||||||
|
>>> Tag.objects.select_related('parent', "parent__category").order_by('name')
|
||||||
|
[<Tag: t1>, <Tag: t2>, <Tag: t3>, <Tag: t4>, <Tag: t5>]
|
||||||
|
|
||||||
Bug #6180, #6203 -- dates with limits and/or counts
|
Bug #6180, #6203 -- dates with limits and/or counts
|
||||||
>>> Item.objects.count()
|
>>> Item.objects.count()
|
||||||
4
|
4
|
||||||
|
|
|
@ -65,6 +65,23 @@ class Client(models.Model):
|
||||||
state = models.ForeignKey(State, null=True)
|
state = models.ForeignKey(State, null=True)
|
||||||
status = models.ForeignKey(ClientStatus)
|
status = models.ForeignKey(ClientStatus)
|
||||||
|
|
||||||
|
# Some model inheritance exercises
|
||||||
|
class Parent(models.Model):
|
||||||
|
name = models.CharField(max_length=10)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Child(Parent):
|
||||||
|
value = models.IntegerField()
|
||||||
|
|
||||||
|
class Item(models.Model):
|
||||||
|
name = models.CharField(max_length=10)
|
||||||
|
child = models.ForeignKey(Child, null=True)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
__test__ = {'API_TESTS': """
|
__test__ = {'API_TESTS': """
|
||||||
Regression test for bug #7110. When using select_related(), we must query the
|
Regression test for bug #7110. When using select_related(), we must query the
|
||||||
Device and Building tables using two different aliases (each) in order to
|
Device and Building tables using two different aliases (each) in order to
|
||||||
|
@ -140,4 +157,12 @@ for country before getting status.
|
||||||
<ClientStatus: ClientStatus object>
|
<ClientStatus: ClientStatus object>
|
||||||
>>> Client.objects.select_related('status')[0].status
|
>>> Client.objects.select_related('status')[0].status
|
||||||
<ClientStatus: ClientStatus object>
|
<ClientStatus: ClientStatus object>
|
||||||
|
|
||||||
|
Exercising select_related() with multi-table model inheritance.
|
||||||
|
>>> c1 = Child.objects.create(name="child1", value=42)
|
||||||
|
>>> _ = Item.objects.create(name="item1", child=c1)
|
||||||
|
>>> _ = Item.objects.create(name="item2")
|
||||||
|
>>> Item.objects.select_related("child").order_by("name")
|
||||||
|
[<Item: item1>, <Item: item2>]
|
||||||
|
|
||||||
"""}
|
"""}
|
||||||
|
|
Loading…
Reference in New Issue