mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #17644 -- Changed Query.alias_map to use namedtuples
This makes the code easier to understand and may even have a benefit in memory usage (namedtuples instead of dicts). Thanks, lrekucki and akaariai
This commit is contained in:
		| @@ -1,7 +1,7 @@ | |||||||
| from itertools import izip | from itertools import izip | ||||||
| from django.db.backends.util import truncate_name, typecast_timestamp | from django.db.backends.util import truncate_name, typecast_timestamp | ||||||
| from django.db.models.sql import compiler | from django.db.models.sql import compiler | ||||||
| from django.db.models.sql.constants import TABLE_NAME, MULTI | from django.db.models.sql.constants import MULTI | ||||||
|  |  | ||||||
| SQLCompiler = compiler.SQLCompiler | SQLCompiler = compiler.SQLCompiler | ||||||
|  |  | ||||||
| @@ -35,7 +35,7 @@ class GeoSQLCompiler(compiler.SQLCompiler): | |||||||
|             for col, field in izip(self.query.select, self.query.select_fields): |             for col, field in izip(self.query.select, self.query.select_fields): | ||||||
|                 if isinstance(col, (list, tuple)): |                 if isinstance(col, (list, tuple)): | ||||||
|                     alias, column = col |                     alias, column = col | ||||||
|                     table = self.query.alias_map[alias][TABLE_NAME] |                     table = self.query.alias_map[alias].table_name | ||||||
|                     if table in only_load and column not in only_load[table]: |                     if table in only_load and column not in only_load[table]: | ||||||
|                         continue |                         continue | ||||||
|                     r = self.get_field_select(field, alias, column) |                     r = self.get_field_select(field, alias, column) | ||||||
| @@ -138,7 +138,7 @@ class GeoSQLCompiler(compiler.SQLCompiler): | |||||||
|                 # aliases will have already been set up in pre_sql_setup(), so |                 # aliases will have already been set up in pre_sql_setup(), so | ||||||
|                 # we can save time here. |                 # we can save time here. | ||||||
|                 alias = self.query.included_inherited_models[model] |                 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 | ||||||
|             if as_pairs: |             if as_pairs: | ||||||
|   | |||||||
| @@ -188,7 +188,7 @@ class SQLCompiler(object): | |||||||
|             for col in self.query.select: |             for col in self.query.select: | ||||||
|                 if isinstance(col, (list, tuple)): |                 if isinstance(col, (list, tuple)): | ||||||
|                     alias, column = col |                     alias, column = col | ||||||
|                     table = self.query.alias_map[alias][TABLE_NAME] |                     table = self.query.alias_map[alias].table_name | ||||||
|                     if table in only_load and column not in only_load[table]: |                     if table in only_load and column not in only_load[table]: | ||||||
|                         continue |                         continue | ||||||
|                     r = '%s.%s' % (qn(alias), qn(column)) |                     r = '%s.%s' % (qn(alias), qn(column)) | ||||||
| @@ -289,7 +289,7 @@ class SQLCompiler(object): | |||||||
|                 # aliases will have already been set up in pre_sql_setup(), so |                 # aliases will have already been set up in pre_sql_setup(), so | ||||||
|                 # we can save time here. |                 # we can save time here. | ||||||
|                 alias = self.query.included_inherited_models[model] |                 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 | ||||||
|             if as_pairs: |             if as_pairs: | ||||||
| @@ -432,7 +432,7 @@ class SQLCompiler(object): | |||||||
|             # Firstly, avoid infinite loops. |             # Firstly, avoid infinite loops. | ||||||
|             if not already_seen: |             if not already_seen: | ||||||
|                 already_seen = set() |                 already_seen = set() | ||||||
|             join_tuple = tuple([self.query.alias_map[j][TABLE_NAME] for j in joins]) |             join_tuple = tuple([self.query.alias_map[j].table_name for j in joins]) | ||||||
|             if join_tuple in already_seen: |             if join_tuple in already_seen: | ||||||
|                 raise FieldError('Infinite loop caused by ordering.') |                 raise FieldError('Infinite loop caused by ordering.') | ||||||
|             already_seen.add(join_tuple) |             already_seen.add(join_tuple) | ||||||
| @@ -470,7 +470,7 @@ class SQLCompiler(object): | |||||||
|         # Ordering or distinct must not affect the returned set, and INNER |         # Ordering or distinct must not affect the returned set, and INNER | ||||||
|         # JOINS for nullable fields could do this. |         # JOINS for nullable fields could do this. | ||||||
|         self.query.promote_alias_chain(joins, |         self.query.promote_alias_chain(joins, | ||||||
|             self.query.alias_map[joins[0]][JOIN_TYPE] == self.query.LOUTER) |             self.query.alias_map[joins[0]].join_type == self.query.LOUTER) | ||||||
|         return field, col, alias, joins, opts |         return field, col, alias, joins, opts | ||||||
|  |  | ||||||
|     def _final_join_removal(self, col, alias): |     def _final_join_removal(self, col, alias): | ||||||
| @@ -485,11 +485,11 @@ class SQLCompiler(object): | |||||||
|         if alias: |         if alias: | ||||||
|             while 1: |             while 1: | ||||||
|                 join = self.query.alias_map[alias] |                 join = self.query.alias_map[alias] | ||||||
|                 if col != join[RHS_JOIN_COL]: |                 if col != join.rhs_join_col: | ||||||
|                     break |                     break | ||||||
|                 self.query.unref_alias(alias) |                 self.query.unref_alias(alias) | ||||||
|                 alias = join[LHS_ALIAS] |                 alias = join.lhs_alias | ||||||
|                 col = join[LHS_JOIN_COL] |                 col = join.lhs_join_col | ||||||
|         return col, alias |         return col, alias | ||||||
|  |  | ||||||
|     def get_from_clause(self): |     def get_from_clause(self): | ||||||
| @@ -641,7 +641,7 @@ class SQLCompiler(object): | |||||||
|                     alias_chain.append(alias) |                     alias_chain.append(alias) | ||||||
|                     for (dupe_opts, dupe_col) in dupe_set: |                     for (dupe_opts, dupe_col) in dupe_set: | ||||||
|                         self.query.update_dupe_avoidance(dupe_opts, dupe_col, alias) |                         self.query.update_dupe_avoidance(dupe_opts, dupe_col, alias) | ||||||
|                 if self.query.alias_map[root_alias][JOIN_TYPE] == self.query.LOUTER: |                 if self.query.alias_map[root_alias].join_type == self.query.LOUTER: | ||||||
|                     self.query.promote_alias_chain(alias_chain, True) |                     self.query.promote_alias_chain(alias_chain, True) | ||||||
|             else: |             else: | ||||||
|                 alias = root_alias |                 alias = root_alias | ||||||
| @@ -659,7 +659,7 @@ class SQLCompiler(object): | |||||||
|             columns, aliases = self.get_default_columns(start_alias=alias, |             columns, aliases = self.get_default_columns(start_alias=alias, | ||||||
|                     opts=f.rel.to._meta, as_pairs=True) |                     opts=f.rel.to._meta, as_pairs=True) | ||||||
|             self.query.related_select_cols.extend(columns) |             self.query.related_select_cols.extend(columns) | ||||||
|             if self.query.alias_map[alias][JOIN_TYPE] == self.query.LOUTER: |             if self.query.alias_map[alias].join_type == self.query.LOUTER: | ||||||
|                 self.query.promote_alias_chain(aliases, True) |                 self.query.promote_alias_chain(aliases, True) | ||||||
|             self.query.related_select_fields.extend(f.rel.to._meta.fields) |             self.query.related_select_fields.extend(f.rel.to._meta.fields) | ||||||
|             if restricted: |             if restricted: | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | from collections import namedtuple | ||||||
| import re | import re | ||||||
|  |  | ||||||
| # Valid query types (a dictionary is used for speedy lookups). | # Valid query types (a dictionary is used for speedy lookups). | ||||||
| @@ -5,7 +6,7 @@ QUERY_TERMS = dict([(x, None) for x in ( | |||||||
|     'exact', 'iexact', 'contains', 'icontains', 'gt', 'gte', 'lt', 'lte', 'in', |     'exact', 'iexact', 'contains', 'icontains', 'gt', 'gte', 'lt', 'lte', 'in', | ||||||
|     'startswith', 'istartswith', 'endswith', 'iendswith', 'range', 'year', |     'startswith', 'istartswith', 'endswith', 'iendswith', 'range', 'year', | ||||||
|     'month', 'day', 'week_day', 'isnull', 'search', 'regex', 'iregex', |     'month', 'day', 'week_day', 'isnull', 'search', 'regex', 'iregex', | ||||||
|     )]) | )]) | ||||||
|  |  | ||||||
| # Size of each "chunk" for get_iterator calls. | # Size of each "chunk" for get_iterator calls. | ||||||
| # Larger values are slightly faster at the expense of more storage space. | # Larger values are slightly faster at the expense of more storage space. | ||||||
| @@ -17,13 +18,9 @@ LOOKUP_SEP = '__' | |||||||
| # Constants to make looking up tuple values clearer. | # Constants to make looking up tuple values clearer. | ||||||
| # Join lists (indexes into the tuples that are values in the alias_map | # Join lists (indexes into the tuples that are values in the alias_map | ||||||
| # dictionary in the Query class). | # dictionary in the Query class). | ||||||
| TABLE_NAME = 0 | JoinInfo = namedtuple('JoinInfo', | ||||||
| RHS_ALIAS = 1 |                       'table_name rhs_alias join_type lhs_alias ' | ||||||
| JOIN_TYPE = 2 |                       'lhs_join_col rhs_join_col nullable') | ||||||
| LHS_ALIAS = 3 |  | ||||||
| LHS_JOIN_COL = 4 |  | ||||||
| RHS_JOIN_COL = 5 |  | ||||||
| NULLABLE = 6 |  | ||||||
|  |  | ||||||
| # How many results to expect from a cursor.execute call | # How many results to expect from a cursor.execute call | ||||||
| MULTI = 'multi' | MULTI = 'multi' | ||||||
| @@ -32,6 +29,5 @@ SINGLE = 'single' | |||||||
| ORDER_PATTERN = re.compile(r'\?|[-+]?[.\w]+$') | ORDER_PATTERN = re.compile(r'\?|[-+]?[.\w]+$') | ||||||
| ORDER_DIR = { | ORDER_DIR = { | ||||||
|     'ASC': ('ASC', 'DESC'), |     'ASC': ('ASC', 'DESC'), | ||||||
|     'DESC': ('DESC', 'ASC')} |     'DESC': ('DESC', 'ASC'), | ||||||
|  | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -101,7 +101,11 @@ class Query(object): | |||||||
|     def __init__(self, model, where=WhereNode): |     def __init__(self, model, where=WhereNode): | ||||||
|         self.model = model |         self.model = model | ||||||
|         self.alias_refcount = SortedDict() |         self.alias_refcount = SortedDict() | ||||||
|         self.alias_map = {}     # Maps alias to join information |         # alias_map is the most important data structure regarding joins. | ||||||
|  |         # It's used for recording which joins exist in the query and what | ||||||
|  |         # type they are. The key is the alias of the joined table (possibly | ||||||
|  |         # the table name) and the value is JoinInfo from constants.py. | ||||||
|  |         self.alias_map = {} | ||||||
|         self.table_map = {}     # Maps table names to list of aliases. |         self.table_map = {}     # Maps table names to list of aliases. | ||||||
|         self.join_map = {} |         self.join_map = {} | ||||||
|         self.default_cols = True |         self.default_cols = True | ||||||
| @@ -686,11 +690,11 @@ class Query(object): | |||||||
|  |  | ||||||
|         Returns True if the join was promoted by this call. |         Returns True if the join was promoted by this call. | ||||||
|         """ |         """ | ||||||
|         if ((unconditional or self.alias_map[alias][NULLABLE]) and |         if ((unconditional or self.alias_map[alias].nullable) and | ||||||
|                 self.alias_map[alias][JOIN_TYPE] != self.LOUTER): |                 self.alias_map[alias].join_type != self.LOUTER): | ||||||
|             data = list(self.alias_map[alias]) |             data = self.alias_map[alias] | ||||||
|             data[JOIN_TYPE] = self.LOUTER |             data = data._replace(join_type=self.LOUTER) | ||||||
|             self.alias_map[alias] = tuple(data) |             self.alias_map[alias] = data | ||||||
|             return True |             return True | ||||||
|         return False |         return False | ||||||
|  |  | ||||||
| @@ -730,7 +734,7 @@ class Query(object): | |||||||
|                 continue |                 continue | ||||||
|             if (alias not in initial_refcounts or |             if (alias not in initial_refcounts or | ||||||
|                     self.alias_refcount[alias] == initial_refcounts[alias]): |                     self.alias_refcount[alias] == initial_refcounts[alias]): | ||||||
|                 parent = self.alias_map[alias][LHS_ALIAS] |                 parent = self.alias_map[alias].lhs_alias | ||||||
|                 must_promote = considered.get(parent, False) |                 must_promote = considered.get(parent, False) | ||||||
|                 promoted = self.promote_alias(alias, must_promote) |                 promoted = self.promote_alias(alias, must_promote) | ||||||
|                 considered[alias] = must_promote or promoted |                 considered[alias] = must_promote or promoted | ||||||
| @@ -767,14 +771,14 @@ class Query(object): | |||||||
|             aliases = tuple([change_map.get(a, a) for a in aliases]) |             aliases = tuple([change_map.get(a, a) for a in aliases]) | ||||||
|             self.join_map[k] = aliases |             self.join_map[k] = aliases | ||||||
|         for old_alias, new_alias in change_map.iteritems(): |         for old_alias, new_alias in change_map.iteritems(): | ||||||
|             alias_data = list(self.alias_map[old_alias]) |             alias_data = self.alias_map[old_alias] | ||||||
|             alias_data[RHS_ALIAS] = new_alias |             alias_data = alias_data._replace(rhs_alias=new_alias) | ||||||
|             self.alias_refcount[new_alias] = self.alias_refcount[old_alias] |             self.alias_refcount[new_alias] = self.alias_refcount[old_alias] | ||||||
|             del self.alias_refcount[old_alias] |             del self.alias_refcount[old_alias] | ||||||
|             self.alias_map[new_alias] = tuple(alias_data) |             self.alias_map[new_alias] = alias_data | ||||||
|             del self.alias_map[old_alias] |             del self.alias_map[old_alias] | ||||||
|  |  | ||||||
|             table_aliases = self.table_map[alias_data[TABLE_NAME]] |             table_aliases = self.table_map[alias_data.table_name] | ||||||
|             for pos, alias in enumerate(table_aliases): |             for pos, alias in enumerate(table_aliases): | ||||||
|                 if alias == old_alias: |                 if alias == old_alias: | ||||||
|                     table_aliases[pos] = new_alias |                     table_aliases[pos] = new_alias | ||||||
| @@ -789,11 +793,10 @@ class Query(object): | |||||||
|  |  | ||||||
|         # 3. Update any joins that refer to the old alias. |         # 3. Update any joins that refer to the old alias. | ||||||
|         for alias, data in self.alias_map.iteritems(): |         for alias, data in self.alias_map.iteritems(): | ||||||
|             lhs = data[LHS_ALIAS] |             lhs = data.lhs_alias | ||||||
|             if lhs in change_map: |             if lhs in change_map: | ||||||
|                 data = list(data) |                 data = data._replace(lhs_alias=change_map[lhs]) | ||||||
|                 data[LHS_ALIAS] = change_map[lhs] |                 self.alias_map[alias] = data | ||||||
|                 self.alias_map[alias] = tuple(data) |  | ||||||
|  |  | ||||||
|     def bump_prefix(self, exceptions=()): |     def bump_prefix(self, exceptions=()): | ||||||
|         """ |         """ | ||||||
| @@ -876,7 +879,7 @@ class Query(object): | |||||||
|         """ |         """ | ||||||
|         lhs, table, lhs_col, col = connection |         lhs, table, lhs_col, col = connection | ||||||
|         if lhs in self.alias_map: |         if lhs in self.alias_map: | ||||||
|             lhs_table = self.alias_map[lhs][TABLE_NAME] |             lhs_table = self.alias_map[lhs].table_name | ||||||
|         else: |         else: | ||||||
|             lhs_table = lhs |             lhs_table = lhs | ||||||
|  |  | ||||||
| @@ -889,11 +892,11 @@ class Query(object): | |||||||
|         if not always_create: |         if not always_create: | ||||||
|             for alias in self.join_map.get(t_ident, ()): |             for alias in self.join_map.get(t_ident, ()): | ||||||
|                 if alias not in exclusions: |                 if alias not in exclusions: | ||||||
|                     if lhs_table and not self.alias_refcount[self.alias_map[alias][LHS_ALIAS]]: |                     if lhs_table and not self.alias_refcount[self.alias_map[alias].lhs_alias]: | ||||||
|                         # The LHS of this join tuple is no longer part of the |                         # The LHS of this join tuple is no longer part of the | ||||||
|                         # query, so skip this possibility. |                         # query, so skip this possibility. | ||||||
|                         continue |                         continue | ||||||
|                     if self.alias_map[alias][LHS_ALIAS] != lhs: |                     if self.alias_map[alias].lhs_alias != lhs: | ||||||
|                         continue |                         continue | ||||||
|                     self.ref_alias(alias) |                     self.ref_alias(alias) | ||||||
|                     if promote: |                     if promote: | ||||||
| @@ -910,7 +913,7 @@ class Query(object): | |||||||
|             join_type = self.LOUTER |             join_type = self.LOUTER | ||||||
|         else: |         else: | ||||||
|             join_type = self.INNER |             join_type = self.INNER | ||||||
|         join = (table, alias, join_type, lhs, lhs_col, col, nullable) |         join = JoinInfo(table, alias, join_type, lhs, lhs_col, col, nullable) | ||||||
|         self.alias_map[alias] = join |         self.alias_map[alias] = join | ||||||
|         if t_ident in self.join_map: |         if t_ident in self.join_map: | ||||||
|             self.join_map[t_ident] += (alias,) |             self.join_map[t_ident] += (alias,) | ||||||
| @@ -1150,7 +1153,7 @@ class Query(object): | |||||||
|                 # also be promoted, regardless of whether they have been |                 # also be promoted, regardless of whether they have been | ||||||
|                 # promoted as a result of this pass through the tables. |                 # promoted as a result of this pass through the tables. | ||||||
|                 unconditional = (unconditional or |                 unconditional = (unconditional or | ||||||
|                     self.alias_map[join][JOIN_TYPE] == self.LOUTER) |                     self.alias_map[join].join_type == self.LOUTER) | ||||||
|                 if join == table and self.alias_refcount[join] > 1: |                 if join == table and self.alias_refcount[join] > 1: | ||||||
|                     # We have more than one reference to this join table. |                     # We have more than one reference to this join table. | ||||||
|                     # This means that we are dealing with two different query |                     # This means that we are dealing with two different query | ||||||
| @@ -1181,8 +1184,8 @@ class Query(object): | |||||||
|             if lookup_type != 'isnull': |             if lookup_type != 'isnull': | ||||||
|                 if len(join_list) > 1: |                 if len(join_list) > 1: | ||||||
|                     for alias in join_list: |                     for alias in join_list: | ||||||
|                         if self.alias_map[alias][JOIN_TYPE] == self.LOUTER: |                         if self.alias_map[alias].join_type == self.LOUTER: | ||||||
|                             j_col = self.alias_map[alias][RHS_JOIN_COL] |                             j_col = self.alias_map[alias].rhs_join_col | ||||||
|                             entry = self.where_class() |                             entry = self.where_class() | ||||||
|                             entry.add( |                             entry.add( | ||||||
|                                 (Constraint(alias, j_col, None), 'isnull', True), |                                 (Constraint(alias, j_col, None), 'isnull', True), | ||||||
| @@ -1510,7 +1513,7 @@ class Query(object): | |||||||
|             join_list = join_list[:penultimate] |             join_list = join_list[:penultimate] | ||||||
|             final = penultimate |             final = penultimate | ||||||
|             penultimate = last.pop() |             penultimate = last.pop() | ||||||
|             col = self.alias_map[extra[0]][LHS_JOIN_COL] |             col = self.alias_map[extra[0]].lhs_join_col | ||||||
|             for alias in extra: |             for alias in extra: | ||||||
|                 self.unref_alias(alias) |                 self.unref_alias(alias) | ||||||
|         else: |         else: | ||||||
| @@ -1518,12 +1521,12 @@ class Query(object): | |||||||
|         alias = join_list[-1] |         alias = join_list[-1] | ||||||
|         while final > 1: |         while final > 1: | ||||||
|             join = self.alias_map[alias] |             join = self.alias_map[alias] | ||||||
|             if (col != join[RHS_JOIN_COL] or join[JOIN_TYPE] != self.INNER or |             if (col != join.rhs_join_col or join.join_type != self.INNER or | ||||||
|                     nonnull_check): |                     nonnull_check): | ||||||
|                 break |                 break | ||||||
|             self.unref_alias(alias) |             self.unref_alias(alias) | ||||||
|             alias = join[LHS_ALIAS] |             alias = join.lhs_alias | ||||||
|             col = join[LHS_JOIN_COL] |             col = join.lhs_join_col | ||||||
|             join_list.pop() |             join_list.pop() | ||||||
|             final -= 1 |             final -= 1 | ||||||
|             if final == penultimate: |             if final == penultimate: | ||||||
| @@ -1646,10 +1649,10 @@ class Query(object): | |||||||
|                 col = target.column |                 col = target.column | ||||||
|                 if len(joins) > 1: |                 if len(joins) > 1: | ||||||
|                     join = self.alias_map[final_alias] |                     join = self.alias_map[final_alias] | ||||||
|                     if col == join[RHS_JOIN_COL]: |                     if col == join.rhs_join_col: | ||||||
|                         self.unref_alias(final_alias) |                         self.unref_alias(final_alias) | ||||||
|                         final_alias = join[LHS_ALIAS] |                         final_alias = join.lhs_alias | ||||||
|                         col = join[LHS_JOIN_COL] |                         col = join.lhs_join_col | ||||||
|                         joins = joins[:-1] |                         joins = joins[:-1] | ||||||
|                 self.promote_alias_chain(joins[1:]) |                 self.promote_alias_chain(joins[1:]) | ||||||
|                 self.select.append((final_alias, col)) |                 self.select.append((final_alias, col)) | ||||||
| @@ -1923,7 +1926,7 @@ class Query(object): | |||||||
|         alias = self.get_initial_alias() |         alias = self.get_initial_alias() | ||||||
|         field, col, opts, joins, last, extra = self.setup_joins( |         field, col, opts, joins, last, extra = self.setup_joins( | ||||||
|                 start.split(LOOKUP_SEP), opts, alias, False) |                 start.split(LOOKUP_SEP), opts, alias, False) | ||||||
|         select_col = self.alias_map[joins[1]][LHS_JOIN_COL] |         select_col = self.alias_map[joins[1]].lhs_join_col | ||||||
|         select_alias = alias |         select_alias = alias | ||||||
|  |  | ||||||
|         # The call to setup_joins added an extra reference to everything in |         # The call to setup_joins added an extra reference to everything in | ||||||
| @@ -1936,12 +1939,12 @@ class Query(object): | |||||||
|         # is *always* the same value as lhs). |         # is *always* the same value as lhs). | ||||||
|         for alias in joins[1:]: |         for alias in joins[1:]: | ||||||
|             join_info = self.alias_map[alias] |             join_info = self.alias_map[alias] | ||||||
|             if (join_info[LHS_JOIN_COL] != select_col |             if (join_info.lhs_join_col != select_col | ||||||
|                     or join_info[JOIN_TYPE] != self.INNER): |                     or join_info.join_type != self.INNER): | ||||||
|                 break |                 break | ||||||
|             self.unref_alias(select_alias) |             self.unref_alias(select_alias) | ||||||
|             select_alias = join_info[RHS_ALIAS] |             select_alias = join_info.rhs_alias | ||||||
|             select_col = join_info[RHS_JOIN_COL] |             select_col = join_info.rhs_join_col | ||||||
|         self.select = [(select_alias, select_col)] |         self.select = [(select_alias, select_col)] | ||||||
|         self.remove_inherited_models() |         self.remove_inherited_models() | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user