Commit ef5f9b6a authored by Matt Robenolt's avatar Matt Robenolt Committed by Tim Graham
Browse files

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

parent 17557d06
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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)
+6 −5
Original line number Diff line number Diff line
@@ -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``

+3 −0
Original line number Diff line number Diff line
@@ -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
^^^^^^^

+15 −0
Original line number Diff line number Diff line
@@ -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):