Commit 68985db4 authored by Anssi Kääriäinen's avatar Anssi Kääriäinen
Browse files

Added Query.join_parent_model()

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

Refs #19385
parent 4511aeb6
Loading
Loading
Loading
Loading
+5 −57
Original line number Diff line number Diff line
@@ -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
+32 −4
Original line number Diff line number Diff line
@@ -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