diff --git a/django/db/backends/base/operations.py b/django/db/backends/base/operations.py index d5a40eb46eed..9f40ec5e4fc5 100644 --- a/django/db/backends/base/operations.py +++ b/django/db/backends/base/operations.py @@ -562,10 +562,6 @@ def adapt_datetimefield_value(self, value): """ if value is None: return None - # Expression values are adapted by the database. - if hasattr(value, "resolve_expression"): - return value - return str(value) def adapt_timefield_value(self, value): @@ -575,10 +571,6 @@ def adapt_timefield_value(self, value): """ if value is None: return None - # Expression values are adapted by the database. - if hasattr(value, "resolve_expression"): - return value - if timezone.is_aware(value): raise ValueError("Django does not support timezone-aware times.") return str(value) diff --git a/django/db/backends/oracle/operations.py b/django/db/backends/oracle/operations.py index 9e8172b80a10..aedfeea2367d 100644 --- a/django/db/backends/oracle/operations.py +++ b/django/db/backends/oracle/operations.py @@ -590,10 +590,6 @@ def adapt_datetimefield_value(self, value): if value is None: return None - # Expression values are adapted by the database. - if hasattr(value, "resolve_expression"): - return value - # oracledb doesn't support tz-aware datetimes if timezone.is_aware(value): if settings.USE_TZ: @@ -610,10 +606,6 @@ def adapt_timefield_value(self, value): if value is None: return None - # Expression values are adapted by the database. - if hasattr(value, "resolve_expression"): - return value - if isinstance(value, str): return datetime.datetime.strptime(value, "%H:%M:%S") diff --git a/django/db/backends/sqlite3/operations.py b/django/db/backends/sqlite3/operations.py index dfc9857b84c3..29a5c0391e40 100644 --- a/django/db/backends/sqlite3/operations.py +++ b/django/db/backends/sqlite3/operations.py @@ -263,10 +263,6 @@ def adapt_datetimefield_value(self, value): if value is None: return None - # Expression values are adapted by the database. - if hasattr(value, "resolve_expression"): - return value - # SQLite doesn't support tz-aware datetimes if timezone.is_aware(value): if settings.USE_TZ: @@ -283,10 +279,6 @@ def adapt_timefield_value(self, value): if value is None: return None - # Expression values are adapted by the database. - if hasattr(value, "resolve_expression"): - return value - # SQLite doesn't support tz-aware datetimes if timezone.is_aware(value): raise ValueError("SQLite backend does not support timezone-aware times.") diff --git a/django/db/models/lookups.py b/django/db/models/lookups.py index c0bcc1b3bf13..4a6e2b324147 100644 --- a/django/db/models/lookups.py +++ b/django/db/models/lookups.py @@ -268,11 +268,16 @@ def get_db_prep_lookup(self, value, connection): getattr(field, "get_db_prep_value", None) or self.lhs.output_field.get_db_prep_value ) + if not self.get_db_prep_lookup_value_is_iterable: + value = [value] return ( "%s", - [get_db_prep_value(v, connection, prepared=True) for v in value] - if self.get_db_prep_lookup_value_is_iterable - else [get_db_prep_value(value, connection, prepared=True)], + [ + v + if hasattr(v, "as_sql") + else get_db_prep_value(v, connection, prepared=True) + for v in value + ], ) diff --git a/tests/backends/base/test_operations.py b/tests/backends/base/test_operations.py index d2a3fb67657f..8df02ee76b44 100644 --- a/tests/backends/base/test_operations.py +++ b/tests/backends/base/test_operations.py @@ -4,7 +4,7 @@ from django.core.management.color import no_style from django.db import NotSupportedError, connection, transaction from django.db.backends.base.operations import BaseDatabaseOperations -from django.db.models import DurationField, Value +from django.db.models import DurationField from django.db.models.expressions import Col from django.db.models.lookups import Exact from django.test import ( @@ -89,17 +89,9 @@ def test_adapt_unknown_value_time(self): def test_adapt_timefield_value_none(self): self.assertIsNone(self.ops.adapt_timefield_value(None)) - def test_adapt_timefield_value_expression(self): - value = Value(timezone.now().time()) - self.assertEqual(self.ops.adapt_timefield_value(value), value) - def test_adapt_datetimefield_value_none(self): self.assertIsNone(self.ops.adapt_datetimefield_value(None)) - def test_adapt_datetimefield_value_expression(self): - value = Value(timezone.now()) - self.assertEqual(self.ops.adapt_datetimefield_value(value), value) - def test_adapt_timefield_value(self): msg = "Django does not support timezone-aware times." with self.assertRaisesMessage(ValueError, msg): diff --git a/tests/expressions/tests.py b/tests/expressions/tests.py index 5ba786db135d..909e317dca11 100644 --- a/tests/expressions/tests.py +++ b/tests/expressions/tests.py @@ -1225,7 +1225,7 @@ def test_expressions_not_introduce_sql_injection_via_untrusted_string_inclusion( queryset = Company.objects.filter(name__in=[F("num_chairs") + "1)) OR ((1==1"]) self.assertQuerySetEqual(queryset, [], ordered=False) - def test_range_lookup_allows_F_expressions_and_expressions_for_datetimes(self): + def test_range_lookup_allows_F_expressions_and_expressions_for_dates(self): start = datetime.datetime(2016, 2, 3, 15, 0, 0) end = datetime.datetime(2016, 2, 5, 15, 0, 0) experiment_1 = Experiment.objects.create( @@ -1256,9 +1256,19 @@ def test_range_lookup_allows_F_expressions_and_expressions_for_datetimes(self): experiment=experiment_2, result_time=datetime.datetime(2016, 1, 8, 5, 0, 0), ) - within_experiment_time = [F("experiment__start"), F("experiment__end")] - queryset = Result.objects.filter(result_time__range=within_experiment_time) - self.assertSequenceEqual(queryset, [r1]) + tests = [ + # Datetimes. + ([F("experiment__start"), F("experiment__end")], "result_time__range"), + # Dates. + ( + [F("experiment__start__date"), F("experiment__end__date")], + "result_time__date__range", + ), + ] + for within_experiment_time, lookup in tests: + with self.subTest(lookup=lookup): + queryset = Result.objects.filter(**{lookup: within_experiment_time}) + self.assertSequenceEqual(queryset, [r1]) class FTests(SimpleTestCase):