1
0
mirror of https://github.com/django/django.git synced 2025-07-06 02:39:12 +00:00

queryset-refactor: Added the ability to apply parameters to the select

fragments in QuerySet.extra(). Refs #2902


git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@6603 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick 2007-10-24 04:22:23 +00:00
parent abcb70e524
commit f951d97d99
4 changed files with 51 additions and 16 deletions

View File

@ -88,7 +88,6 @@ class _QuerySet(object):
max_depth = self.query.max_depth
index_end = len(self.model._meta.fields)
extra_select = self.query.extra_select.keys()
extra_select.sort()
for row in self.query.results_iter():
if fill_cache:
obj, index_end = get_cached_row(klass=self.model, row=row,
@ -378,11 +377,7 @@ class ValuesQuerySet(QuerySet):
# names of the model fields to select.
def iterator(self):
extra_select = self.query.extra_select.keys()
extra_select.sort()
if extra_select:
self.field_names.extend([f for f in extra_select])
self.field_names.extend([f for f in self.query.extra_select.keys()])
for row in self.query.results_iter():
yield dict(zip(self.field_names, row))
@ -409,7 +404,7 @@ class ValuesQuerySet(QuerySet):
except KeyError, e:
raise FieldDoesNotExist('%s has no field named %r'
% (opts.object_name, e.args[0]))
field_names = self._fields
field_names = list(self._fields)
else:
fields = []
field_names = []

View File

@ -10,7 +10,8 @@ all about the internals of models in order to get the information it needs.
import copy
import re
from django.utils import tree
from django.utils.tree import Node
from django.utils.datastructures import SortedDict
from django.db.models.sql.where import WhereNode, AND, OR
from django.db.models.sql.datastructures import Count, Date
from django.db.models.fields import FieldDoesNotExist, Field
@ -94,7 +95,7 @@ class Query(object):
# These are for extensions. The contents are more or less appended
# verbatim to the appropriate clause.
self.extra_select = {} # Maps col_alias -> col_sql.
self.extra_select = SortedDict() # Maps col_alias -> col_sql.
self.extra_tables = []
self.extra_where = []
self.extra_params = []
@ -364,12 +365,8 @@ class Query(object):
for f in self.model._meta.fields]
aliases = result[:]
# We sort extra_select so that the result columns are in a well-defined
# order (and thus QuerySet.iterator can extract them correctly).
extra_select = self.extra_select.items()
extra_select.sort()
result.extend(['(%s) AS %s' % (col, alias)
for alias, col in extra_select])
for alias, col in self.extra_select.items()])
aliases.extend(self.extra_select.keys())
self._select_aliases = dict.fromkeys(aliases)
@ -761,7 +758,7 @@ class Query(object):
return
for child in q_object.children:
if isinstance(child, tree.Node):
if isinstance(child, Node):
self.where.start_subtree(q_object.connection)
self.add_q(child)
self.where.end_subtree()
@ -937,7 +934,7 @@ class Query(object):
# level.
self.distinct = False
self.select = [select]
self.extra_select = {}
self.extra_select = SortedDict()
def execute_sql(self, result_type=MULTI):
"""

View File

@ -820,6 +820,34 @@ of the arguments is required, but you should use at least one of them.
some database backends, such as some MySQL versions, don't support
subqueries.
**New in Django development version**
In some rare cases, you might wish to pass parameters to the SQL fragments
in ``extra(select=...)```. Since the ``params`` attribute is a sequence
and the ``select`` attribute is a dictionary, some care is required so
that the parameters are matched up correctly with the extra select pieces.
Firstly, in this situation, you should use a
``django.utils.datastructures.SortedDict`` for the ``select`` value, not
just a normal Python dictionary. Secondly, make sure that your parameters
for the ``select`` come first in the list and that you have not passed any
parameters to an earlier ``extra()`` call for this queryset.
This will work::
Blog.objects.extra(
select=SortedDict(('a', '%s'), ('b', '%s')),
params=('one', 'two'))
... while this won't::
# Will not work!
Blog.objects.extra(where=['foo=%s'], params=('bar',)).extra(
select=SortedDict(('a', '%s'), ('b', '%s')),
params=('one', 'two'))
In the second example, the earlier ``params`` usage will mess up the later
one. So always put your extra select pieces in the first ``extra()`` call
if you need to use parameters in them.
``where`` / ``tables``
You can define explicit SQL ``WHERE`` clauses -- perhaps to perform
non-explicit joins -- by using ``where``. You can manually add tables to

View File

@ -379,5 +379,20 @@ The all() method on querysets returns a copy of the queryset.
>>> q1 = Item.objects.order_by('name')
>>> id(q1) == id(q1.all())
False
Bug #2902
Parameters can be given to extra_select, *if* you use a SortedDict.
(First we need to know which order the keys fall in "naturally" on your system,
so we can put things in the wrong way around from normal. A normal dict would
thus fail.)
>>> from django.utils.datastructures import SortedDict
>>> s = [('a', '%s'), ('b', '%s')]
>>> params = ['one', 'two']
>>> if {'a': 1, 'b': 2}.keys() == ['a', 'b']:
... s.reverse()
... params.reverse()
>>> Item.objects.extra(select=SortedDict(s), params=params).values('a','b')[0]
{'a': u'one', 'b': u'two'}
"""}