Repository: django/django

Issue:
# Refs #35972 -- Returned params in a tuple in further expressions.

#### Trac ticket number
ticket-35972

#### Branch description
As [pointed out](https://github.com/django/django/pull/20300#discussion_r2643723457) by Simon, `Value.as_sql` still returned params in a list.

#### AI Assistance Disclosure (REQUIRED)
- [x] **No AI tools were used** in preparing this PR.
- [ ] **If AI tools were used**, I have disclosed which ones, and fully reviewed and verified their output.

#### Checklist
- [x] This PR follows the [contribution guidelines](https://docs.djangoproject.com/en/stable/internals/contributing/writing-code/submitting-patches/).
- [x] This PR **does not** disclose a security vulnerability (see [vulnerability reporting](https://docs.djangoproject.com/en/stable/internals/security/)).
- [x] This PR targets the `main` branch. <!-- Backports will be evaluated and done by mergers, when necessary. -->
- [x] The commit message is written in past tense, mentions the ticket number, and ends with a period.
- [ ] I have checked the "Has patch" ticket flag in the Trac system.
- [ ] I have added or updated relevant tests.
- [ ] I have added or updated relevant docs, including release notes if applicable.
- [ ] I have attached screenshots in both light and dark modes for any UI changes.


Implementation patch:
diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py
index 63d0c1802b49..d0e91f13d278 100644
--- a/django/db/models/expressions.py
+++ b/django/db/models/expressions.py
@@ -769,7 +769,7 @@ def as_sql(self, compiler, connection):
         # order of precedence
         expression_wrapper = "(%s)"
         sql = connection.ops.combine_expression(self.connector, expressions)
-        return expression_wrapper % sql, expression_params
+        return expression_wrapper % sql, tuple(expression_params)
 
     def resolve_expression(
         self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False
@@ -835,7 +835,7 @@ def as_sql(self, compiler, connection):
         # order of precedence
         expression_wrapper = "(%s)"
         sql = connection.ops.combine_duration_expression(self.connector, expressions)
-        return expression_wrapper % sql, expression_params
+        return expression_wrapper % sql, tuple(expression_params)
 
     def as_sqlite(self, compiler, connection, **extra_context):
         sql, params = self.as_sql(compiler, connection, **extra_context)
@@ -1179,8 +1179,8 @@ def as_sql(self, compiler, connection):
             # oracledb does not always convert None to the appropriate
             # NULL type (like in case expressions using numbers), so we
             # use a literal SQL NULL
-            return "NULL", []
-        return "%s", [val]
+            return "NULL", ()
+        return "%s", (val,)
 
     def as_sqlite(self, compiler, connection, **extra_context):
         sql, params = self.as_sql(compiler, connection, **extra_context)
@@ -1273,7 +1273,7 @@ def __repr__(self):
         return "'*'"
 
     def as_sql(self, compiler, connection):
-        return "*", []
+        return "*", ()
 
 
 class DatabaseDefault(Expression):
@@ -1313,7 +1313,7 @@ def resolve_expression(
     def as_sql(self, compiler, connection):
         if not connection.features.supports_default_keyword_in_insert:
             return compiler.compile(self.expression)
-        return "DEFAULT", []
+        return "DEFAULT", ()
 
 
 class Col(Expression):
@@ -1398,7 +1398,7 @@ def as_sql(self, compiler, connection):
             cols_sql.append(sql)
             cols_params.extend(params)
 
-        return ", ".join(cols_sql), cols_params
+        return ", ".join(cols_sql), tuple(cols_params)
 
     def relabeled_clone(self, relabels):
         return self.__class__(
@@ -1447,7 +1447,7 @@ def relabeled_clone(self, relabels):
         return clone
 
     def as_sql(self, compiler, connection):
-        return connection.ops.quote_name(self.refs), []
+        return connection.ops.quote_name(self.refs), ()
 
     def get_group_by_cols(self):
         return [self]
@@ -1764,7 +1764,7 @@ def as_sql(
         sql = template % template_params
         if self._output_field_or_none is not None:
             sql = connection.ops.unification_cast_sql(self.output_field) % sql
-        return sql, sql_params
+        return sql, tuple(sql_params)
 
     def get_group_by_cols(self):
         if not self.cases:
@@ -2148,7 +2148,7 @@ def as_sql(self, compiler, connection):
                 "end": end,
                 "exclude": self.get_exclusion(),
             },
-            [],
+            (),
         )
 
     def __repr__(self):


Test patch:
diff --git a/tests/expressions/tests.py b/tests/expressions/tests.py
index cb62d0fbd73f..5effc8ac0d17 100644
--- a/tests/expressions/tests.py
+++ b/tests/expressions/tests.py
@@ -2504,9 +2504,9 @@ def test_compile_unresolved(self):
         # This test might need to be revisited later on if #25425 is enforced.
         compiler = Time.objects.all().query.get_compiler(connection=connection)
         value = Value("foo")
-        self.assertEqual(value.as_sql(compiler, connection), ("%s", ["foo"]))
+        self.assertEqual(value.as_sql(compiler, connection), ("%s", ("foo",)))
         value = Value("foo", output_field=CharField())
-        self.assertEqual(value.as_sql(compiler, connection), ("%s", ["foo"]))
+        self.assertEqual(value.as_sql(compiler, connection), ("%s", ("foo",)))
 
     def test_output_field_decimalfield(self):
         Time.objects.create()