From 8dde0504f3771ff2707bdddb702eaf5c06bb77a7 Mon Sep 17 00:00:00 2001 From: David Sanders Date: Mon, 30 Oct 2023 20:44:44 +1100 Subject: [PATCH] [5.0.x] Fixed #34936 -- Fixed migration crash for DecimalField with db_default on SQLite. CAST() must be wrapped in parentheses to be recognized as an expression on SQLite. Regression in 7414704e88d73dafbcfbb85f9bc54cb6111439d3. Backport of 797957fb48f63760442d3d4c8985fd94f8602d5e from main --- django/db/backends/oracle/features.py | 4 ++++ django/db/models/expressions.py | 2 +- tests/field_defaults/models.py | 4 ++++ tests/field_defaults/tests.py | 9 ++++++--- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/django/db/backends/oracle/features.py b/django/db/backends/oracle/features.py index 8a02e098d6..bb7b2384bd 100644 --- a/django/db/backends/oracle/features.py +++ b/django/db/backends/oracle/features.py @@ -119,6 +119,10 @@ class DatabaseFeatures(BaseDatabaseFeatures): "Oracle doesn't support comparing NCLOB to NUMBER.": { "generic_relations_regress.tests.GenericRelationTests.test_textlink_filter", }, + "DecimalField.db_default doesn't return decimal.Decimal instances on Oracle " + "(#34941).": { + "field_defaults.tests.DefaultTests.test_field_db_defaults_returning", + }, } django_test_expected_failures = { # A bug in Django/oracledb with respect to string handling (#23843). diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index 9aa78a281f..b38f8f64ef 100644 --- a/django/db/models/expressions.py +++ b/django/db/models/expressions.py @@ -27,7 +27,7 @@ class SQLiteNumericMixin: sql, params = self.as_sql(compiler, connection, **extra_context) try: if self.output_field.get_internal_type() == "DecimalField": - sql = "CAST(%s AS NUMERIC)" % sql + sql = "(CAST(%s AS NUMERIC))" % sql except FieldError: pass return sql, params diff --git a/tests/field_defaults/models.py b/tests/field_defaults/models.py index 5f9c38a5a4..17191deefb 100644 --- a/tests/field_defaults/models.py +++ b/tests/field_defaults/models.py @@ -10,6 +10,7 @@ field. """ from datetime import datetime +from decimal import Decimal from django.db import models from django.db.models.functions import Coalesce, ExtractYear, Now, Pi @@ -33,6 +34,9 @@ class DBArticle(models.Model): headline = models.CharField(max_length=100, db_default="Default headline") pub_date = models.DateTimeField(db_default=Now()) + cost = models.DecimalField( + max_digits=3, decimal_places=2, db_default=Decimal("3.33") + ) class Meta: required_db_features = {"supports_expression_defaults"} diff --git a/tests/field_defaults/tests.py b/tests/field_defaults/tests.py index 76d01f7a5a..c05d966bdb 100644 --- a/tests/field_defaults/tests.py +++ b/tests/field_defaults/tests.py @@ -1,4 +1,5 @@ from datetime import datetime +from decimal import Decimal from math import pi from django.db import connection @@ -44,6 +45,7 @@ class DefaultTests(TestCase): self.assertIsInstance(a.id, int) self.assertEqual(a.headline, "Default headline") self.assertIsInstance(a.pub_date, datetime) + self.assertEqual(a.cost, Decimal("3.33")) @skipIfDBFeature("can_return_columns_from_insert") @skipUnlessDBFeature("supports_expression_defaults") @@ -54,6 +56,7 @@ class DefaultTests(TestCase): self.assertIsInstance(a.id, int) self.assertEqual(a.headline, "Default headline") self.assertIsInstance(a.pub_date, datetime) + self.assertEqual(a.cost, Decimal("3.33")) def test_null_db_default(self): obj1 = DBDefaults.objects.create() @@ -141,12 +144,12 @@ class DefaultTests(TestCase): articles = [DBArticle(pub_date=pub_date), DBArticle(pub_date=pub_date)] DBArticle.objects.bulk_create(articles) - headlines = DBArticle.objects.values_list("headline", "pub_date") + headlines = DBArticle.objects.values_list("headline", "pub_date", "cost") self.assertSequenceEqual( headlines, [ - ("Default headline", pub_date), - ("Default headline", pub_date), + ("Default headline", pub_date, Decimal("3.33")), + ("Default headline", pub_date, Decimal("3.33")), ], )