Fixed #32231 -- Allowed passing None params to QuerySet.raw().

This commit is contained in:
Alexander Lyabah 2020-11-28 18:08:27 +02:00 committed by Mariusz Felisiak
parent aa3d360631
commit 415f50298f
5 changed files with 28 additions and 7 deletions

View File

@ -818,7 +818,7 @@ class QuerySet:
# PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS # # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
################################################## ##################################################
def raw(self, raw_query, params=None, translations=None, using=None): def raw(self, raw_query, params=(), translations=None, using=None):
if using is None: if using is None:
using = self.db using = self.db
qs = RawQuerySet(raw_query, model=self.model, params=params, translations=translations, using=using) qs = RawQuerySet(raw_query, model=self.model, params=params, translations=translations, using=using)
@ -1419,14 +1419,14 @@ class RawQuerySet:
Provide an iterator which converts the results of raw SQL queries into Provide an iterator which converts the results of raw SQL queries into
annotated model instances. annotated model instances.
""" """
def __init__(self, raw_query, model=None, query=None, params=None, def __init__(self, raw_query, model=None, query=None, params=(),
translations=None, using=None, hints=None): translations=None, using=None, hints=None):
self.raw_query = raw_query self.raw_query = raw_query
self.model = model self.model = model
self._db = using self._db = using
self._hints = hints or {} self._hints = hints or {}
self.query = query or sql.RawQuery(sql=raw_query, using=self.db, params=params) self.query = query or sql.RawQuery(sql=raw_query, using=self.db, params=params)
self.params = params or () self.params = params
self.translations = translations or {} self.translations = translations or {}
self._result_cache = None self._result_cache = None
self._prefetch_related_lookups = () self._prefetch_related_lookups = ()

View File

@ -69,8 +69,8 @@ JoinInfo = namedtuple(
class RawQuery: class RawQuery:
"""A single raw SQL query.""" """A single raw SQL query."""
def __init__(self, sql, using, params=None): def __init__(self, sql, using, params=()):
self.params = params or () self.params = params
self.sql = sql self.sql = sql
self.using = using self.using = using
self.cursor = None self.cursor = None
@ -111,9 +111,13 @@ class RawQuery:
@property @property
def params_type(self): def params_type(self):
if self.params is None:
return None
return dict if isinstance(self.params, Mapping) else tuple return dict if isinstance(self.params, Mapping) else tuple
def __str__(self): def __str__(self):
if self.params_type is None:
return self.sql
return self.sql % self.params_type(self.params) return self.sql % self.params_type(self.params)
def _execute_query(self): def _execute_query(self):
@ -127,6 +131,8 @@ class RawQuery:
params = tuple(adapter(val) for val in self.params) params = tuple(adapter(val) for val in self.params)
elif params_type is dict: elif params_type is dict:
params = {key: adapter(val) for key, val in self.params.items()} params = {key: adapter(val) for key, val in self.params.items()}
elif params_type is None:
params = None
else: else:
raise RuntimeError("Unexpected params type: %s" % params_type) raise RuntimeError("Unexpected params type: %s" % params_type)

View File

@ -1843,7 +1843,7 @@ raised if ``select_for_update()`` is used in autocommit mode.
``raw()`` ``raw()``
~~~~~~~~~ ~~~~~~~~~
.. method:: raw(raw_query, params=None, translations=None) .. method:: raw(raw_query, params=(), translations=None)
Takes a raw SQL query, executes it, and returns a Takes a raw SQL query, executes it, and returns a
``django.db.models.query.RawQuerySet`` instance. This ``RawQuerySet`` instance ``django.db.models.query.RawQuerySet`` instance. This ``RawQuerySet`` instance
@ -1858,6 +1858,11 @@ See the :doc:`/topics/db/sql` for more information.
filtering. As such, it should generally be called from the ``Manager`` or filtering. As such, it should generally be called from the ``Manager`` or
from a fresh ``QuerySet`` instance. from a fresh ``QuerySet`` instance.
.. versionchanged:: 3.2
The default value of the ``params`` argument was changed from ``None`` to
an empty tuple.
Operators that return new ``QuerySet``\s Operators that return new ``QuerySet``\s
---------------------------------------- ----------------------------------------

View File

@ -43,7 +43,7 @@ Performing raw queries
The ``raw()`` manager method can be used to perform raw SQL queries that The ``raw()`` manager method can be used to perform raw SQL queries that
return model instances: return model instances:
.. method:: Manager.raw(raw_query, params=None, translations=None) .. method:: Manager.raw(raw_query, params=(), translations=None)
This method takes a raw SQL query, executes it, and returns a This method takes a raw SQL query, executes it, and returns a
``django.db.models.query.RawQuerySet`` instance. This ``RawQuerySet`` instance ``django.db.models.query.RawQuerySet`` instance. This ``RawQuerySet`` instance
@ -99,6 +99,11 @@ make it very powerful.
both rows will match. To prevent this, perform the correct typecasting both rows will match. To prevent this, perform the correct typecasting
before using the value in a query. before using the value in a query.
.. versionchanged:: 3.2
The default value of the ``params`` argument was changed from ``None`` to
an empty tuple.
Mapping query fields to model fields Mapping query fields to model fields
------------------------------------ ------------------------------------

View File

@ -180,6 +180,11 @@ class RawQueryTests(TestCase):
self.assertEqual(len(results), 1) self.assertEqual(len(results), 1)
self.assertIsInstance(repr(qset), str) self.assertIsInstance(repr(qset), str)
def test_params_none(self):
query = "SELECT * FROM raw_query_author WHERE first_name like 'J%'"
qset = Author.objects.raw(query, params=None)
self.assertEqual(len(qset), 2)
def test_escaped_percent(self): def test_escaped_percent(self):
query = "SELECT * FROM raw_query_author WHERE first_name like 'J%%'" query = "SELECT * FROM raw_query_author WHERE first_name like 'J%%'"
qset = Author.objects.raw(query) qset = Author.objects.raw(query)