Changeset 8426
- Timestamp:
- 08/17/08 15:07:59 (5 months ago)
- Files:
-
- django/trunk/django/db/models/sql/query.py (modified) (8 diffs)
- django/trunk/docs/db-api.txt (modified) (1 diff)
- django/trunk/tests/regressiontests/extra_regress/models.py (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/trunk/django/db/models/sql/query.py
r8223 r8426 12 12 from django.utils.tree import Node 13 13 from django.utils.datastructures import SortedDict 14 from django.utils.encoding import force_unicode 14 15 from django.db import connection 15 16 from django.db.models import signals … … 78 79 # These are for extensions. The contents are more or less appended 79 80 # verbatim to the appropriate clause. 80 self.extra_select = {} # Maps col_alias -> col_sql. 81 self.extra_select_params = () 81 self.extra_select = SortedDict() # Maps col_alias -> col_sql. 82 82 self.extra_tables = () 83 83 self.extra_where = () … … 182 182 obj.max_depth = self.max_depth 183 183 obj.extra_select = self.extra_select.copy() 184 obj.extra_select_params = self.extra_select_params185 184 obj.extra_tables = self.extra_tables 186 185 obj.extra_where = self.extra_where … … 227 226 distinct=False) 228 227 obj.select = [] 229 obj.extra_select = {}228 obj.extra_select = SortedDict() 230 229 obj.add_count_column() 231 230 data = obj.execute_sql(SINGLE) … … 260 259 261 260 where, w_params = self.where.as_sql(qn=self.quote_name_unless_alias) 262 params = list(self.extra_select_params) 261 params = [] 262 for val in self.extra_select.itervalues(): 263 params.extend(val[1]) 263 264 264 265 result = ['SELECT'] … … 414 415 qn = self.quote_name_unless_alias 415 416 qn2 = self.connection.ops.quote_name 416 result = ['(%s) AS %s' % (col , qn2(alias)) for alias, col in self.extra_select.iteritems()]417 result = ['(%s) AS %s' % (col[0], qn2(alias)) for alias, col in self.extra_select.iteritems()] 417 418 aliases = set(self.extra_select.keys()) 418 419 if with_aliases: … … 1511 1512 self.select_fields = [None] 1512 1513 self.extra_select = {} 1513 self.extra_select_params = ()1514 1514 1515 1515 def add_select_related(self, fields): … … 1534 1534 """ 1535 1535 if select: 1536 # The extra select might be ordered (because it will be accepting 1537 # parameters). 1538 if (isinstance(select, SortedDict) and 1539 not isinstance(self.extra_select, SortedDict)): 1540 self.extra_select = SortedDict(self.extra_select) 1541 self.extra_select.update(select) 1542 if select_params: 1543 self.extra_select_params += tuple(select_params) 1536 # We need to pair any placeholder markers in the 'select' 1537 # dictionary with their parameters in 'select_params' so that 1538 # subsequent updates to the select dictionary also adjust the 1539 # parameters appropriately. 1540 select_pairs = SortedDict() 1541 if select_params: 1542 param_iter = iter(select_params) 1543 else: 1544 param_iter = iter([]) 1545 for name, entry in select.items(): 1546 entry = force_unicode(entry) 1547 entry_params = [] 1548 pos = entry.find("%s") 1549 while pos != -1: 1550 entry_params.append(param_iter.next()) 1551 pos = entry.find("%s", pos + 2) 1552 select_pairs[name] = (entry, entry_params) 1553 # This is order preserving, since self.extra_select is a SortedDict. 1554 self.extra_select.update(select_pairs) 1544 1555 if where: 1545 1556 self.extra_where += tuple(where) django/trunk/docs/db-api.txt
r8406 r8426 1015 1015 select_params=('one', 'two')) 1016 1016 1017 The only thing to be careful about when using select parameters in 1018 ``extra()`` is to avoid using the substring ``"%%s"`` (that's *two* 1019 percent characters before the ``s``) in the select strings. Django's 1020 tracking of parameters looks for ``%s`` and an escaped ``%`` character 1021 like this isn't detected. That will lead to incorrect results. 1022 1017 1023 ``where`` / ``tables`` 1018 1024 You can define explicit SQL ``WHERE`` clauses -- perhaps to perform django/trunk/tests/regressiontests/extra_regress/models.py
r7832 r8426 1 1 import copy 2 2 3 from django.contrib.auth.models import User 3 4 from django.db import models 4 5 from django.db.models.query import Q 6 from django.utils.datastructures import SortedDict 5 7 6 8 … … 24 26 25 27 __test__ = {"API_TESTS": """ 26 # ##Regression tests for #7314 and #737228 # Regression tests for #7314 and #7372 27 29 28 30 >>> rm = RevisionableModel.objects.create(title='First Revision') … … 53 55 [<RevisionableModel: Second Revision (2, 1)>] 54 56 57 >>> u = User.objects.create_user(username="fred", password="secret", email="fred@example.com") 58 59 # General regression tests: extra select parameters should stay tied to their 60 # corresponding select portions. Applies when portions are updated or otherwise 61 # moved around. 62 >>> qs = User.objects.extra(select=SortedDict((("alpha", "%s"), ("beta", "2"), ("gamma", "%s"))), select_params=(1, 3)) 63 >>> qs = qs.extra(select={"beta": 4}) 64 >>> qs = qs.extra(select={"alpha": "%s"}, select_params=[5]) 65 >>> result = {'alpha': 5, 'beta': 4, 'gamma': 3} 66 >>> list(qs.filter(id=u.id).values('alpha', 'beta', 'gamma')) == [result] 67 True 68 69 # Regression test for #7957: Combining extra() calls should leave the 70 # corresponding parameters associated with the right extra() bit. I.e. internal 71 # dictionary must remain sorted. 72 >>> User.objects.extra(select={"alpha": "%s"}, select_params=(1,)).extra(select={"beta": "%s"}, select_params=(2,))[0].alpha 73 1 74 >>> User.objects.extra(select={"beta": "%s"}, select_params=(1,)).extra(select={"alpha": "%s"}, select_params=(2,))[0].alpha 75 2 76 77 # Regression test for #7961: When not using a portion of an extra(...) in a 78 # query, remove any corresponding parameters from the query as well. 79 >>> list(User.objects.extra(select={"alpha": "%s"}, select_params=(-6,)).filter(id=u.id).values_list('id', flat=True)) == [u.id] 80 True 81 55 82 """}
