Skip to content

Instantly share code, notes, and snippets.

@mitchellrj
Created December 4, 2013 17:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mitchellrj/7792290 to your computer and use it in GitHub Desktop.
Save mitchellrj/7792290 to your computer and use it in GitHub Desktop.
Quick and dirty monkey-patch for Django issue #21554. https://code.djangoproject.com/ticket/21554
def patch_21554():
"""See https://code.djangoproject.com/ticket/21554 """
try:
from collections import UserDict
except ImportError:
from UserDict import UserDict
class OneToOneDict(UserDict):
def __init__(self, *args, **kwargs):
self.reverse = {}
UserDict.__init__(self, *args, **kwargs)
def __setitem__(self, key, value):
self.reverse[value] = key
UserDict.__setitem__(self, key, value)
def __delitem__(self, key):
value = self.data[key]
del self.reverse[value]
UserDict.__delitem__(self, key)
def pop(self, key, *args):
value = UserDict.pop(self, key, *args)
del self.reverse[value]
return value
def popitem(self):
value = UserDict.popitem(self)
del self.reverse[value]
return value
def update(self, dict=None, **kwargs):
if dict is None:
pass
elif isinstance(dict, OneToOneDict):
self.reverse.update(dict.reverse)
else:
for k, v in dict.items():
self.reverse[v] = k
if len(kwargs):
self.reverse.update(dict(map(lambda i: (i[1], i[0]),
kwargs.items())))
UserDict.update(self, dict, **kwargs)
def inverse_get(self, key, default=None):
return self.reverse.get(key, default)
from django.db.models.sql import compiler
def get_default_columns(self, with_aliases=False, col_aliases=None,
start_alias=None, opts=None, as_pairs=False, from_parent=None):
"""
Computes the default columns for selecting every field in the base
model. Will sometimes be called to pull in related models (e.g. via
select_related), in which case "opts" and "start_alias" will be given
to provide a starting point for the traversal.
Returns a list of strings, quoted appropriately for use in SQL
directly, as well as a set of aliases used in the select statement (if
'as_pairs' is True, returns a list of (alias, col_name) pairs instead
of strings as the first component and None as the second component).
"""
result = []
if opts is None:
opts = self.query.get_meta()
qn = self.quote_name_unless_alias
qn2 = self.connection.ops.quote_name
aliases = set()
only_load = self.deferred_to_columns()
if not start_alias:
start_alias = self.query.get_initial_alias()
# The 'seen_models' is used to optimize checking the needed parent
# alias for a given field. This also includes None -> start_alias to
# be used by local fields.
seen_models = OneToOneDict({None: start_alias})
for field, model in opts.get_concrete_fields_with_model():
if from_parent and model is not None and issubclass(from_parent,
model):
# Avoid loading data for already loaded parents.
continue
alias = self.query.join_parent_model(opts, model, start_alias,
seen_models)
column = field.column
target_model = seen_models.inverse_get(alias)
if target_model is not None:
ancestor_link = target_model._meta.get_ancestor_link(model)
if ancestor_link:
column = ancestor_link.column
table = self.query.alias_map[alias].table_name
if table in only_load and column not in only_load[table]:
continue
if as_pairs:
result.append((alias, column))
aliases.add(alias)
continue
if with_aliases and column in col_aliases:
c_alias = 'Col%d' % len(col_aliases)
result.append('%s.%s AS %s' % (qn(alias),
qn2(column), c_alias))
col_aliases.add(c_alias)
aliases.add(c_alias)
else:
r = '%s.%s' % (qn(alias), qn2(column))
result.append(r)
aliases.add(r)
if with_aliases:
col_aliases.add(column)
return result, aliases
compiler.SQLCompiler.get_default_columns = get_default_columns
patch_21554()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment