Fixed #23460 -- Added literal `%s` support to extra() QuerySets.

This commit is contained in:
Matt Robenolt 2014-09-10 07:23:58 +00:00 committed by Tim Graham
parent 17557d068c
commit ef5f9b6ae8
4 changed files with 26 additions and 6 deletions

View File

@ -1775,6 +1775,7 @@ class Query(object):
entry_params = [] entry_params = []
pos = entry.find("%s") pos = entry.find("%s")
while pos != -1: while pos != -1:
if pos == 0 or entry[pos - 1] != '%':
entry_params.append(next(param_iter)) entry_params.append(next(param_iter))
pos = entry.find("%s", pos + 2) pos = entry.find("%s", pos + 2)
select_pairs[name] = (entry, entry_params) select_pairs[name] = (entry, entry_params)

View File

@ -1144,11 +1144,12 @@ of the arguments is required, but you should use at least one of them.
select=OrderedDict([('a', '%s'), ('b', '%s')]), select=OrderedDict([('a', '%s'), ('b', '%s')]),
select_params=('one', 'two')) select_params=('one', 'two'))
The only thing to be careful about when using select parameters in If you need to use a literal ``%s`` inside your select string, use
``extra()`` is to avoid using the substring ``"%%s"`` (that's *two* the sequence ``%%s``.
percent characters before the ``s``) in the select strings. Django's
tracking of parameters looks for ``%s`` and an escaped ``%`` character .. versionchanged:: 1.8
like this isn't detected. That will lead to incorrect results.
Prior to 1.8, you were unable to escape a literal ``%s``.
* ``where`` / ``tables`` * ``where`` / ``tables``

View File

@ -281,6 +281,9 @@ Models
Django uses whenever objects are loaded using the ORM. The method allows Django uses whenever objects are loaded using the ORM. The method allows
customizing model loading behavior. customizing model loading behavior.
* ``extra(select={...})`` now allows you to escape a literal ``%s`` sequence
using ``%%s``.
Signals Signals
^^^^^^^ ^^^^^^^

View File

@ -1655,6 +1655,21 @@ class Queries5Tests(TestCase):
['<Note: n1>', '<Note: n2>'] ['<Note: n1>', '<Note: n2>']
) )
def test_extra_select_literal_percent_s(self):
# Allow %%s to escape select clauses
self.assertEqual(
Note.objects.extra(select={'foo': "'%%s'"})[0].foo,
'%s'
)
self.assertEqual(
Note.objects.extra(select={'foo': "'%%s bar %%s'"})[0].foo,
'%s bar %s'
)
self.assertEqual(
Note.objects.extra(select={'foo': "'bar %%s'"})[0].foo,
'bar %s'
)
class SelectRelatedTests(TestCase): class SelectRelatedTests(TestCase):
def test_tickets_3045_3288(self): def test_tickets_3045_3288(self):