Added Query.join_parent_model()

This simplifies especially compiler.py a lot, where almost the same
code was repeated multiple times.

Refs #19385
This commit is contained in:
Anssi Kääriäinen 2012-12-17 14:02:41 +02:00
parent 4511aeb6b8
commit 68985db482
2 changed files with 37 additions and 61 deletions

View File

@ -261,27 +261,14 @@ class SQLCompiler(object):
qn2 = self.connection.ops.quote_name qn2 = self.connection.ops.quote_name
aliases = set() aliases = set()
only_load = self.deferred_to_columns() only_load = self.deferred_to_columns()
seen = self.query.included_inherited_models.copy()
if start_alias: if start_alias:
seen = {None: start_alias} seen[None] = start_alias
for field, model in opts.get_fields_with_model(): for field, model in opts.get_fields_with_model():
if from_parent and model is not None and issubclass(from_parent, model): if from_parent and model is not None and issubclass(from_parent, model):
# Avoid loading data for already loaded parents. # Avoid loading data for already loaded parents.
continue continue
if start_alias: alias = self.query.join_parent_model(opts, model, start_alias, seen)
try:
alias = seen[model]
except KeyError:
link_field = opts.get_ancestor_link(model)
alias = self.query.join((start_alias, model._meta.db_table,
link_field.column, model._meta.pk.column),
join_field=link_field)
seen[model] = alias
else:
# If we're starting from the base model of the queryset, the
# aliases will have already been set up in pre_sql_setup(), so
# we can save time here.
alias = self.query.included_inherited_models[model]
table = self.query.alias_map[alias].table_name table = self.query.alias_map[alias].table_name
if table in only_load and field.column not in only_load[table]: if table in only_load and field.column not in only_load[table]:
continue continue
@ -623,26 +610,7 @@ class SQLCompiler(object):
continue continue
table = f.rel.to._meta.db_table table = f.rel.to._meta.db_table
promote = nullable or f.null promote = nullable or f.null
if model: alias = self.query.join_parent_model(opts, model, root_alias, {})
int_opts = opts
alias = root_alias
alias_chain = []
for int_model in opts.get_base_chain(model):
# 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
link_field = int_opts.get_ancestor_link(int_model)
int_opts = int_model._meta
alias = self.query.join((alias, int_opts.db_table, lhs_col,
int_opts.pk.column), promote=promote, join_field=link_field)
alias_chain.append(alias)
else:
alias = root_alias
alias = self.query.join((alias, table, f.column, alias = self.query.join((alias, table, f.column,
f.rel.get_related_field().column), f.rel.get_related_field().column),
@ -670,28 +638,8 @@ class SQLCompiler(object):
only_load.get(model), reverse=True): only_load.get(model), reverse=True):
continue continue
alias = self.query.join_parent_model(opts, f.rel.to, root_alias, {})
table = model._meta.db_table 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
link_field = int_opts.get_ancestor_link(int_model)
int_opts = int_model._meta
alias = self.query.join(
(alias, int_opts.db_table, lhs_col, int_opts.pk.column),
promote=True, join_field=link_field
)
alias_chain.append(alias)
alias = self.query.join( alias = self.query.join(
(alias, table, f.rel.get_related_field().column, f.column), (alias, table, f.rel.get_related_field().column, f.column),
promote=True, join_field=f promote=True, join_field=f

View File

@ -1004,12 +1004,40 @@ class Query(object):
for field, model in opts.get_fields_with_model(): for field, model in opts.get_fields_with_model():
if model not in seen: if model not in seen:
link_field = opts.get_ancestor_link(model) self.join_parent_model(opts, model, root_alias, seen)
seen[model] = self.join(
(root_alias, model._meta.db_table, link_field.column,
model._meta.pk.column), join_field=link_field)
self.included_inherited_models = seen self.included_inherited_models = seen
def join_parent_model(self, opts, model, alias, seen):
"""
Makes sure the given 'model' is joined in the query. If 'model' isn't
a parent of 'opts' or if it is None this method is a no-op.
The 'alias' is the root alias for starting the join, 'seen' is a dict
of model -> alias of existing joins.
"""
if model in seen:
return seen[model]
int_opts = opts
chain = opts.get_base_chain(model)
if chain is None:
return alias
for int_model in chain:
if int_model in seen:
return seen[int_model]
# 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
link_field = int_opts.get_ancestor_link(int_model)
int_opts = int_model._meta
connection = (alias, int_opts.db_table, link_field.column, int_opts.pk.column)
alias = seen[int_model] = self.join(connection, nullable=False,
join_field=link_field)
return alias
def remove_inherited_models(self): def remove_inherited_models(self):
""" """
Undoes the effects of setup_inherited_models(). Should be called Undoes the effects of setup_inherited_models(). Should be called