summaryrefslogtreecommitdiff
path: root/django/db/models/sql/compiler.py
diff options
context:
space:
mode:
authorRussell Keith-Magee <russell@keith-magee.com>2010-01-27 13:30:29 +0000
committerRussell Keith-Magee <russell@keith-magee.com>2010-01-27 13:30:29 +0000
commit58cd220f51d5e294cb9e67c12a6e9d08523e282f (patch)
treec87c968bb69215449924efe57e8b13d70acd4fa3 /django/db/models/sql/compiler.py
parent8e8d4b5888b73e5c0b2cfc77be4c6d5898546654 (diff)
Fixed #7270 -- Added the ability to follow reverse OneToOneFields in select_related(). Thanks to George Vilches, Ben Davis, and Alex Gaynor for their work on various stages of this patch.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@12307 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/db/models/sql/compiler.py')
-rw-r--r--django/db/models/sql/compiler.py68
1 files changed, 67 insertions, 1 deletions
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
index 6a95d32259..1625a0e6c9 100644
--- a/django/db/models/sql/compiler.py
+++ b/django/db/models/sql/compiler.py
@@ -520,7 +520,7 @@ class SQLCompiler(object):
# Setup for the case when only particular related fields should be
# included in the related selection.
- if requested is None and restricted is not False:
+ if requested is None:
if isinstance(self.query.select_related, dict):
requested = self.query.select_related
restricted = True
@@ -600,6 +600,72 @@ class SQLCompiler(object):
self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1,
used, next, restricted, new_nullable, dupe_set, avoid)
+ if restricted:
+ related_fields = [
+ (o.field, o.model)
+ for o in opts.get_all_related_objects()
+ if o.field.unique
+ ]
+ for f, model in related_fields:
+ if not select_related_descend(f, restricted, requested, reverse=True):
+ continue
+ # The "avoid" set is aliases we want to avoid just for this
+ # particular branch of the recursion. They aren't permanently
+ # forbidden from reuse in the related selection tables (which is
+ # what "used" specifies).
+ avoid = avoid_set.copy()
+ dupe_set = orig_dupe_set.copy()
+ table = model._meta.db_table
+
+ int_opts = opts
+ alias = root_alias
+ alias_chain = []
+ chain = opts.get_base_chain(f.rel.to)
+ if chain is not None:
+ for int_model in chain:
+ # Proxy model have elements in base chain
+ # with no parents, assign the new options
+ # object and skip to the next base in that
+ # case
+ if not int_opts.parents[int_model]:
+ int_opts = int_model._meta
+ continue
+ lhs_col = int_opts.parents[int_model].column
+ dedupe = lhs_col in opts.duplicate_targets
+ if dedupe:
+ avoid.update(self.query.dupe_avoidance.get(id(opts), lhs_col),
+ ())
+ dupe_set.add((opts, lhs_col))
+ int_opts = int_model._meta
+ alias = self.query.join(
+ (alias, int_opts.db_table, lhs_col, int_opts.pk.column),
+ exclusions=used, promote=True, reuse=used
+ )
+ alias_chain.append(alias)
+ for dupe_opts, dupe_col in dupe_set:
+ self.query.update_dupe_avoidance(dupe_opts, dupe_col, alias)
+ dedupe = f.column in opts.duplicate_targets
+ if dupe_set or dedupe:
+ avoid.update(self.query.dupe_avoidance.get((id(opts), f.column), ()))
+ if dedupe:
+ dupe_set.add((opts, f.column))
+ alias = self.query.join(
+ (alias, table, f.rel.get_related_field().column, f.column),
+ exclusions=used.union(avoid),
+ promote=True
+ )
+ used.add(alias)
+ columns, aliases = self.get_default_columns(start_alias=alias,
+ opts=model._meta, as_pairs=True)
+ self.query.related_select_cols.extend(columns)
+ self.query.related_select_fields.extend(model._meta.fields)
+
+ next = requested.get(f.related_query_name(), {})
+ new_nullable = f.null or None
+
+ self.fill_related_selections(model._meta, table, cur_depth+1,
+ used, next, restricted, new_nullable)
+
def deferred_to_columns(self):
"""
Converts the self.deferred_loading data structure to mapping of table