Browse Source

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

Alexander Lyabah 4 years ago
parent
commit
415f50298f

+ 3 - 3
django/db/models/query.py

@@ -818,7 +818,7 @@ class QuerySet:
     # 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:
             using = self.db
         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
     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):
         self.raw_query = raw_query
         self.model = model
         self._db = using
         self._hints = hints or {}
         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._result_cache = None
         self._prefetch_related_lookups = ()

+ 8 - 2
django/db/models/sql/query.py

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

+ 6 - 1
docs/ref/models/querysets.txt

@@ -1843,7 +1843,7 @@ raised if ``select_for_update()`` is used in autocommit mode.
 ``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
 ``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
   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
 ----------------------------------------
 

+ 6 - 1
docs/topics/db/sql.txt

@@ -43,7 +43,7 @@ Performing raw queries
 The ``raw()`` manager method can be used to perform raw SQL queries that
 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
 ``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
     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
 ------------------------------------
 

+ 5 - 0
tests/raw_query/tests.py

@@ -180,6 +180,11 @@ class RawQueryTests(TestCase):
         self.assertEqual(len(results), 1)
         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):
         query = "SELECT * FROM raw_query_author WHERE first_name like 'J%%'"
         qset = Author.objects.raw(query)