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:
parent
4511aeb6b8
commit
68985db482
|
@ -261,27 +261,14 @@ class SQLCompiler(object):
|
|||
qn2 = self.connection.ops.quote_name
|
||||
aliases = set()
|
||||
only_load = self.deferred_to_columns()
|
||||
|
||||
seen = self.query.included_inherited_models.copy()
|
||||
if start_alias:
|
||||
seen = {None: start_alias}
|
||||
seen[None] = start_alias
|
||||
for field, model in opts.get_fields_with_model():
|
||||
if from_parent and model is not None and issubclass(from_parent, model):
|
||||
# Avoid loading data for already loaded parents.
|
||||
continue
|
||||
if start_alias:
|
||||
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]
|
||||
alias = self.query.join_parent_model(opts, model, start_alias, seen)
|
||||
table = self.query.alias_map[alias].table_name
|
||||
if table in only_load and field.column not in only_load[table]:
|
||||
continue
|
||||
|
@ -623,26 +610,7 @@ class SQLCompiler(object):
|
|||
continue
|
||||
table = f.rel.to._meta.db_table
|
||||
promote = nullable or f.null
|
||||
if model:
|
||||
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_parent_model(opts, model, root_alias, {})
|
||||
|
||||
alias = self.query.join((alias, table, f.column,
|
||||
f.rel.get_related_field().column),
|
||||
|
@ -670,28 +638,8 @@ class SQLCompiler(object):
|
|||
only_load.get(model), reverse=True):
|
||||
continue
|
||||
|
||||
alias = self.query.join_parent_model(opts, f.rel.to, root_alias, {})
|
||||
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, table, f.rel.get_related_field().column, f.column),
|
||||
promote=True, join_field=f
|
||||
|
|
|
@ -1004,12 +1004,40 @@ class Query(object):
|
|||
|
||||
for field, model in opts.get_fields_with_model():
|
||||
if model not in seen:
|
||||
link_field = opts.get_ancestor_link(model)
|
||||
seen[model] = self.join(
|
||||
(root_alias, model._meta.db_table, link_field.column,
|
||||
model._meta.pk.column), join_field=link_field)
|
||||
self.join_parent_model(opts, model, root_alias, 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):
|
||||
"""
|
||||
Undoes the effects of setup_inherited_models(). Should be called
|
||||
|
|
Loading…
Reference in New Issue