Browse Source

Fixed #34547 -- Deprecated DatabaseOperations.field_cast_sql().

David Smith 1 year ago
parent
commit
27b399d235

+ 9 - 0
django/db/backends/base/operations.py

@@ -1,6 +1,7 @@
 import datetime
 import decimal
 import json
+import warnings
 from importlib import import_module
 
 import sqlparse
@@ -10,6 +11,7 @@ from django.db import NotSupportedError, transaction
 from django.db.backends import utils
 from django.db.models.expressions import Col
 from django.utils import timezone
+from django.utils.deprecation import RemovedInDjango60Warning
 from django.utils.encoding import force_str
 
 
@@ -220,6 +222,13 @@ class BaseDatabaseOperations:
         it in a WHERE statement. The resulting string should contain a '%s'
         placeholder for the column being searched against.
         """
+        warnings.warn(
+            (
+                "DatabaseOperations.field_cast_sql() is deprecated use "
+                "DatabaseOperations.lookup_cast() instead."
+            ),
+            RemovedInDjango60Warning,
+        )
         return "%s"
 
     def force_no_ordering(self):

+ 19 - 2
django/db/models/lookups.py

@@ -1,7 +1,9 @@
 import itertools
 import math
+import warnings
 
 from django.core.exceptions import EmptyResultSet, FullResultSet
+from django.db.backends.base.operations import BaseDatabaseOperations
 from django.db.models.expressions import Case, Expression, Func, Value, When
 from django.db.models.fields import (
     BooleanField,
@@ -13,6 +15,7 @@ from django.db.models.fields import (
 )
 from django.db.models.query_utils import RegisterLookupMixin
 from django.utils.datastructures import OrderedSet
+from django.utils.deprecation import RemovedInDjango60Warning
 from django.utils.functional import cached_property
 from django.utils.hashable import make_hashable
 
@@ -217,8 +220,22 @@ class BuiltinLookup(Lookup):
     def process_lhs(self, compiler, connection, lhs=None):
         lhs_sql, params = super().process_lhs(compiler, connection, lhs)
         field_internal_type = self.lhs.output_field.get_internal_type()
-        db_type = self.lhs.output_field.db_type(connection=connection)
-        lhs_sql = connection.ops.field_cast_sql(db_type, field_internal_type) % lhs_sql
+        if (
+            hasattr(connection.ops.__class__, "field_cast_sql")
+            and connection.ops.__class__.field_cast_sql
+            is not BaseDatabaseOperations.field_cast_sql
+        ):
+            warnings.warn(
+                (
+                    "The usage of DatabaseOperations.field_cast_sql() is deprecated. "
+                    "Implement DatabaseOperations.lookup_cast() instead."
+                ),
+                RemovedInDjango60Warning,
+            )
+            db_type = self.lhs.output_field.db_type(connection=connection)
+            lhs_sql = (
+                connection.ops.field_cast_sql(db_type, field_internal_type) % lhs_sql
+            )
         lhs_sql = (
             connection.ops.lookup_cast(self.lookup_name, field_internal_type) % lhs_sql
         )

+ 2 - 0
docs/internals/deprecation.txt

@@ -40,6 +40,8 @@ details on these changes.
 
 * Support for ``cx_Oracle`` will be removed.
 
+* ``BaseDatabaseOperations.field_cast_sql()`` will be removed.
+
 .. _deprecation-removed-in-5.1:
 
 5.1

+ 5 - 0
docs/releases/5.0.txt

@@ -663,6 +663,11 @@ Miscellaneous
 * Support for ``cx_Oracle`` is deprecated in favor of `oracledb`_ 1.3.2+ Python
   driver.
 
+* ``DatabaseOperations.field_cast_sql()`` is deprecated in favor of
+  ``DatabaseOperations.lookup_cast()``. Starting with Django 6.0,
+  ``BuiltinLookup.process_lhs()`` will no longer call ``field_cast_sql()``.
+  Third-party database backends should implement ``lookup_cast()`` instead.
+
 .. _`oracledb`: https://oracle.github.io/python-oracledb/
 
 Features removed in 5.0

+ 24 - 0
tests/backends/base/test_operations.py

@@ -1,10 +1,12 @@
 import decimal
+from unittest import mock
 
 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.expressions import Col
+from django.db.models.lookups import Exact
 from django.test import (
     SimpleTestCase,
     TestCase,
@@ -13,6 +15,7 @@ from django.test import (
     skipIfDBFeature,
 )
 from django.utils import timezone
+from django.utils.deprecation import RemovedInDjango60Warning
 
 from ..models import Author, Book
 
@@ -235,3 +238,24 @@ class SqlFlushTests(TransactionTestCase):
                 self.assertEqual(author.pk, 1)
                 book = Book.objects.create(author=author)
                 self.assertEqual(book.pk, 1)
+
+
+class DeprecationTests(TestCase):
+    def test_field_cast_sql_warning(self):
+        base_ops = BaseDatabaseOperations(connection=connection)
+        msg = (
+            "DatabaseOperations.field_cast_sql() is deprecated use "
+            "DatabaseOperations.lookup_cast() instead."
+        )
+        with self.assertRaisesMessage(RemovedInDjango60Warning, msg):
+            base_ops.field_cast_sql("integer", "IntegerField")
+
+    def test_field_cast_sql_usage_warning(self):
+        compiler = Author.objects.all().query.get_compiler(connection.alias)
+        msg = (
+            "The usage of DatabaseOperations.field_cast_sql() is deprecated. Implement "
+            "DatabaseOperations.lookup_cast() instead."
+        )
+        with mock.patch.object(connection.ops.__class__, "field_cast_sql"):
+            with self.assertRaisesMessage(RemovedInDjango60Warning, msg):
+                Exact("name", "book__author__name").as_sql(compiler, connection)