Repository: django/django

Issue:
# Fixed #36030 -- Fixed precision loss in division of Decimal literals on SQLite.

#### Trac ticket number
ticket-36030

#### Branch description

On SQLite, ``SQLiteNumericMixin.as_sqlite()`` currently wraps all ``DecimalField`` expressions in ``CAST(... AS NUMERIC)``, including literal ``Value(Decimal(...))`` expressions.
For these literals, the cast is unnecessary and differs from how other backends pass Decimal parameters.

The solution decouples ``Value`` from ``SQLiteNumericMixin`` by implementing a dedicated ``as_sqlite`` method that explicitly casts ``Decimal`` literals to ``REAL``, ensuring correct floating-point arithmetic is used. To prevent regressions, the implementation retains the existing ``CAST(... AS NUMERIC)`` wrapper for ``non-Decimal`` types (such as integers) when they are output to a DecimalField.

**Tests**
A new regression test, ``test_decimal_division_literal_value``, has been added to ``BasicExpressionsTests`` to verify that division yields the correct fractional precision on all backends.

#### Checklist
- [x] This PR targets the `main` branch.
- [x] The commit message is written in past tense, mentions the ticket number, and ends with a period.
- [x] I have checked the "Has patch" flag on the Trac ticket.
- [x] I have added or updated relevant tests.
- [ ] I have added or updated relevant docs, including release notes if applicable.
- [ ] I have attached screenshots in light/dark modes for UI changes (N/A).


Implementation patch:
diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py
index 6b90a42cf1d2..baa91cc2c173 100644
--- a/django/db/models/expressions.py
+++ b/django/db/models/expressions.py
@@ -1141,7 +1141,7 @@ def allowed_default(self):
 
 
 @deconstructible(path="django.db.models.Value")
-class Value(SQLiteNumericMixin, Expression):
+class Value(Expression):
     """Represent a wrapped value as a node within an expression."""
 
     # Provide a default value for `for_save` in order to allow unresolved
@@ -1182,6 +1182,18 @@ def as_sql(self, compiler, connection):
             return "NULL", []
         return "%s", [val]
 
+    def as_sqlite(self, compiler, connection, **extra_context):
+        sql, params = self.as_sql(compiler, connection, **extra_context)
+        try:
+            if self.output_field.get_internal_type() == "DecimalField":
+                if isinstance(self.value, Decimal):
+                    sql = "(CAST(%s AS REAL))" % sql
+                else:
+                    sql = "(CAST(%s AS NUMERIC))" % sql
+        except FieldError:
+            pass
+        return sql, params
+
     def resolve_expression(
         self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False
     ):


Test patch:
diff --git a/tests/expressions/tests.py b/tests/expressions/tests.py
index 981d84e9e8ec..02126fa89612 100644
--- a/tests/expressions/tests.py
+++ b/tests/expressions/tests.py
@@ -137,6 +137,16 @@ def test_annotate_values_aggregate(self):
         )
         self.assertEqual(companies["result"], 2395)
 
+    def test_decimal_division_literal_value(self):
+        """
+        Division with a literal Decimal value preserves precision.
+        """
+        num = Number.objects.create(integer=2)
+        obj = Number.objects.annotate(
+            val=F("integer") / Value(Decimal("3.0"), output_field=DecimalField())
+        ).get(pk=num.pk)
+        self.assertAlmostEqual(obj.val, Decimal("0.6667"), places=4)
+
     def test_annotate_values_filter(self):
         companies = (
             Company.objects.annotate(