Kaydet (Commit) 31d9dc07 authored tarafından Jacob Kaplan-Moss's avatar Jacob Kaplan-Moss

Fixed #7369: fixed a corner-case involving select_related() following non-null…

Fixed #7369: fixed a corner-case involving select_related() following non-null FKs after null ones. Thanks, George Vilches


git-svn-id: http://code.djangoproject.com/svn/django/trunk@7597 bcc190cf-cafb-0310-a4f2-bffc1f526a37
üst 8398ea66
...@@ -851,7 +851,7 @@ class Query(object): ...@@ -851,7 +851,7 @@ class Query(object):
return alias return alias
def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1, def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1,
used=None, requested=None, restricted=None): used=None, requested=None, restricted=None, nullable=None):
""" """
Fill in the information needed for a select_related query. The current Fill in the information needed for a select_related query. The current
depth is measured as the number of connections away from the root model depth is measured as the number of connections away from the root model
...@@ -883,6 +883,10 @@ class Query(object): ...@@ -883,6 +883,10 @@ class Query(object):
(not restricted and f.null) or f.rel.parent_link): (not restricted and f.null) or f.rel.parent_link):
continue continue
table = f.rel.to._meta.db_table table = f.rel.to._meta.db_table
if nullable or f.null:
promote = True
else:
promote = False
if model: if model:
int_opts = opts int_opts = opts
alias = root_alias alias = root_alias
...@@ -891,12 +895,12 @@ class Query(object): ...@@ -891,12 +895,12 @@ class Query(object):
int_opts = int_model._meta int_opts = int_model._meta
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=f.null) promote=promote)
else: else:
alias = root_alias alias = root_alias
alias = self.join((alias, table, f.column, alias = self.join((alias, table, f.column,
f.rel.get_related_field().column), exclusions=used, f.rel.get_related_field().column), exclusions=used,
promote=f.null) promote=promote)
used.add(alias) used.add(alias)
self.related_select_cols.extend([(alias, f2.column) self.related_select_cols.extend([(alias, f2.column)
for f2 in f.rel.to._meta.fields]) for f2 in f.rel.to._meta.fields])
...@@ -905,8 +909,12 @@ class Query(object): ...@@ -905,8 +909,12 @@ class Query(object):
next = requested.get(f.name, {}) next = requested.get(f.name, {})
else: else:
next = False next = False
if f.null is not None:
new_nullable = f.null
else:
new_nullable = None
self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1, self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1,
used, next, restricted) used, next, restricted, new_nullable)
def add_filter(self, filter_expr, connector=AND, negate=False, trim=False, def add_filter(self, filter_expr, connector=AND, negate=False, trim=False,
can_reuse=None): can_reuse=None):
......
"""
Regression tests for proper working of ForeignKey(null=True). Tests these bugs:
* #7369: FK non-null after null relationship on select_related() generates an invalid query
"""
from django.db import models
class SystemInfo(models.Model):
system_name = models.CharField(max_length=32)
class Forum(models.Model):
system_info = models.ForeignKey(SystemInfo)
forum_name = models.CharField(max_length=32)
class Post(models.Model):
forum = models.ForeignKey(Forum, null=True)
title = models.CharField(max_length=32)
def __unicode__(self):
return self.title
class Comment(models.Model):
post = models.ForeignKey(Post, null=True)
comment_text = models.CharField(max_length=250)
def __unicode__(self):
return self.comment_text
__test__ = {'API_TESTS':"""
>>> s = SystemInfo.objects.create(system_name='First forum')
>>> f = Forum.objects.create(system_info=s, forum_name='First forum')
>>> p = Post.objects.create(forum=f, title='First Post')
>>> c1 = Comment.objects.create(post=p, comment_text='My first comment')
>>> c2 = Comment.objects.create(comment_text='My second comment')
# Starting from comment, make sure that a .select_related(...) with a specified
# set of fields will properly LEFT JOIN multiple levels of NULLs (and the things
# that come after the NULLs, or else data that should exist won't).
>>> c = Comment.objects.select_related().get(id=1)
>>> c.post
<Post: First Post>
>>> c = Comment.objects.select_related().get(id=2)
>>> print c.post
None
>>> comments = Comment.objects.select_related('post__forum__system_info').all()
>>> [(c.id, c.post.id) for c in comments]
[(1, 1), (2, None)]
>>> [(c.comment_text, c.post.title) for c in comments]
[(u'My first comment', u'First Post'), (u'My second comment', None)]
"""}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment