From 68985db48212c701a3d975636123a5d79bdc006f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anssi=20K=C3=A4=C3=A4ri=C3=A4inen?= Date: Mon, 17 Dec 2012 14:02:41 +0200 Subject: [PATCH] Added Query.join_parent_model() This simplifies especially compiler.py a lot, where almost the same code was repeated multiple times. Refs #19385 --- django/db/models/sql/compiler.py | 62 +++----------------------------- django/db/models/sql/query.py | 36 ++++++++++++++++--- 2 files changed, 37 insertions(+), 61 deletions(-) diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index b0fb933705..16aa031e59 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -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 diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index e5833b2b51..2546d6c889 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -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