Loading django/db/models/sql/query.py +2 −1 Original line number Diff line number Diff line Loading @@ -1775,6 +1775,7 @@ class Query(object): entry_params = [] pos = entry.find("%s") while pos != -1: if pos == 0 or entry[pos - 1] != '%': entry_params.append(next(param_iter)) pos = entry.find("%s", pos + 2) select_pairs[name] = (entry, entry_params) Loading docs/ref/models/querysets.txt +6 −5 Original line number Diff line number Diff line Loading @@ -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_params=('one', 'two')) The only thing to be careful about when using select parameters in ``extra()`` is to avoid using the substring ``"%%s"`` (that's *two* percent characters before the ``s``) in the select strings. Django's tracking of parameters looks for ``%s`` and an escaped ``%`` character like this isn't detected. That will lead to incorrect results. If you need to use a literal ``%s`` inside your select string, use the sequence ``%%s``. .. versionchanged:: 1.8 Prior to 1.8, you were unable to escape a literal ``%s``. * ``where`` / ``tables`` Loading docs/releases/1.8.txt +3 −0 Original line number Diff line number Diff line Loading @@ -281,6 +281,9 @@ Models Django uses whenever objects are loaded using the ORM. The method allows customizing model loading behavior. * ``extra(select={...})`` now allows you to escape a literal ``%s`` sequence using ``%%s``. Signals ^^^^^^^ Loading tests/queries/tests.py +15 −0 Original line number Diff line number Diff line Loading @@ -1655,6 +1655,21 @@ class Queries5Tests(TestCase): ['<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): def test_tickets_3045_3288(self): Loading Loading
django/db/models/sql/query.py +2 −1 Original line number Diff line number Diff line Loading @@ -1775,6 +1775,7 @@ class Query(object): entry_params = [] pos = entry.find("%s") while pos != -1: if pos == 0 or entry[pos - 1] != '%': entry_params.append(next(param_iter)) pos = entry.find("%s", pos + 2) select_pairs[name] = (entry, entry_params) Loading
docs/ref/models/querysets.txt +6 −5 Original line number Diff line number Diff line Loading @@ -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_params=('one', 'two')) The only thing to be careful about when using select parameters in ``extra()`` is to avoid using the substring ``"%%s"`` (that's *two* percent characters before the ``s``) in the select strings. Django's tracking of parameters looks for ``%s`` and an escaped ``%`` character like this isn't detected. That will lead to incorrect results. If you need to use a literal ``%s`` inside your select string, use the sequence ``%%s``. .. versionchanged:: 1.8 Prior to 1.8, you were unable to escape a literal ``%s``. * ``where`` / ``tables`` Loading
docs/releases/1.8.txt +3 −0 Original line number Diff line number Diff line Loading @@ -281,6 +281,9 @@ Models Django uses whenever objects are loaded using the ORM. The method allows customizing model loading behavior. * ``extra(select={...})`` now allows you to escape a literal ``%s`` sequence using ``%%s``. Signals ^^^^^^^ Loading
tests/queries/tests.py +15 −0 Original line number Diff line number Diff line Loading @@ -1655,6 +1655,21 @@ class Queries5Tests(TestCase): ['<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): def test_tickets_3045_3288(self): Loading