Done. The first version of this patch didn't have the "Be forgiving of
mistakes" section; but I noticed that when I tried to use it, my first
attempt at calling the function that *I myself had written* used
"fields='question'" where I had meant "fields=['question']". Since I
had written the code not ten minutes ago, and I'd already made that
mistake, it must be a very natural mistake to make. And the meaning is
unambiguous, so might as well be forgiving.
Patch below. Comments appreciated. If it looks good, I'll open a ticket for it.
Index: django/core/meta.py
===================================================================
--- django/core/meta.py (revision 331)
+++ django/core/meta.py (working copy)
@@ -562,6 +562,12 @@
new_mod.get_iterator = curry(function_get_iterator, opts, new_class)
new_mod.get_iterator.__doc__ = "Returns an iterator of %s
objects matching the given parameters." % name
+ new_mod.get_values = curry(function_get_values_list, opts, new_class)
+ new_mod.get_values.__doc__ = "Returns a list of dicts
matching the given parameters."
+
+ new_mod.get_values_iterator =
curry(function_get_values_iterator, opts, new_class)
+ new_mod.get_values_iterator.__doc__ = "Returns an iterator of
dicts matching the given parameters."
+
new_mod.get_count = curry(function_get_count, opts)
new_mod.get_count.__doc__ = "Returns the number of %s objects
matching the given parameters." % name
@@ -1047,6 +1053,37 @@
setattr(obj, f.rel.get_cache_name(), rel_obj)
return obj, index_end
+def function_get_values_iterator(opts, klass, **kwargs):
+ """
+ Return SELECT results as a list of dicts rather than as objects.
+ Example use: "SELECT DISTINCT year, month FROM blog ORDER BY year, month"
+ """
+ # kwargs['select'] is a dictionary, and dictionaries' key order is
+ # undefined, so we convert it to a list of tuples internally.
+ kwargs['select'] = kwargs.get('select', {}).items()
+
+ cursor = db.db.cursor()
+ _, sql, params = function_get_sql_clause(opts, **kwargs)
+ # Allow user to specify a specific list of fields to fetch
+ fields = kwargs.get('fields')
+ if isinstance(fields, basestring):
+ # Be forgiving of mistakes: convert to tuple
+ fields = (fields,)
+ if not fields:
+ # Default to all fields
+ fields = [
f.name for f in opts.fields]
+ select = ['%s.%s' % (opts.db_table, f) for f in fields]
+ cursor.execute("SELECT " + (kwargs.get('distinct') and "DISTINCT
" or "") + ",".join(select) + sql, params)
+ while 1:
+ rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
+ if not rows:
+ raise StopIteration
+ for row in rows:
+ yield dict(zip(fields, row))
+
+def function_get_values_list(opts, klass, **kwargs):
+ return list(function_get_values_iterator(opts, klass, **kwargs))
+
def function_get_iterator(opts, klass, **kwargs):
# kwargs['select'] is a dictionary, and dictionaries' key order is
# undefined, so we convert it to a list of tuples internally.
@@ -1117,7 +1154,7 @@
# table_count is used to ensure table aliases are unique.
tables, join_where, where, params = [], [], [], []
for kwarg, kwarg_value in kwarg_items:
- if kwarg in ('order_by', 'limit', 'offset', 'select_related',
'distinct', 'select', 'tables', 'where', 'params'):
+ if kwarg in ('order_by', 'limit', 'offset', 'select_related',
'distinct', 'select', 'tables', 'where', 'params', 'fields'):
continue
if kwarg_value is None:
continue