summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCha Hwa Young <chahwayoung214@gmail.com>2025-10-13 02:29:15 +0900
committerJacob Walls <jacobtylerwalls@gmail.com>2025-11-26 08:36:13 -0500
commit31bc5c2d11e1d1cc728666d0785eea088b1dc1f2 (patch)
tree90019a9d8c2ec569924f187ba138940e03051b4b
parent93fd01d641147d35768e129fcd0163f78e07807b (diff)
[5.2.x] Fixed #31506 -- Clarified that ExpressionWrapper does not perform database casts.
Added warning in DateField documentation about type differences when using timedelta on PostgreSQL and MySQL. Mentioned Cast() and integer arithmetic solutions. Backport of 55af4749b9a48b2978e893e7d7be313c0b2abdb1 from main.
-rw-r--r--docs/ref/models/expressions.txt12
-rw-r--r--docs/ref/models/fields.txt33
2 files changed, 45 insertions, 0 deletions
diff --git a/docs/ref/models/expressions.txt b/docs/ref/models/expressions.txt
index b6512bff72..8879ee1a8f 100644
--- a/docs/ref/models/expressions.txt
+++ b/docs/ref/models/expressions.txt
@@ -37,6 +37,12 @@ determine the result's field type, such as complex expressions that mix field
types. For example, adding a ``DecimalField()`` and a ``FloatField()`` requires
an output field, like ``output_field=FloatField()``.
+``output_field`` also allows using custom fields that perform type conversions
+outside a specific model field context. For example, if you frequently need to
+perform date arithmetic with ``timedelta``, you can create a custom field that
+handles the conversion, ensuring consistent results across databases. See
+:doc:`/howto/custom-model-fields`.
+
Some examples
=============
@@ -559,6 +565,12 @@ available on other expressions. ``ExpressionWrapper`` is necessary when using
arithmetic on ``F()`` expressions with different types as described in
:ref:`using-f-with-annotations`.
+.. admonition:: Database casting not performed
+
+ ``ExpressionWrapper`` only sets the output field for the ORM and does not
+ perform any database-level casting. To ensure a specific type is returned
+ from the database, use :class:`~django.db.models.functions.Cast` instead.
+
Conditional expressions
-----------------------
diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt
index de0b888f82..98efcf6b76 100644
--- a/docs/ref/models/fields.txt
+++ b/docs/ref/models/fields.txt
@@ -841,6 +841,39 @@ Any combination of these options will result in an error.
instance, removing its time component. This is true for both storage and
comparison.
+.. warning::
+
+ On PostgreSQL and MySQL, arithmetic operations on a ``DateField`` with a
+ :class:`~datetime.timedelta` return a ``datetime`` instead of a ``date``.
+ This occurs because Python's ``timedelta`` is converted to SQL
+ ``INTERVAL``, and the SQL operation ``date +/- interval`` returns a
+ ``timestamp`` on these databases.
+
+ To ensure a ``date`` result, use one of the following approaches. Either
+ explicitly cast the result to a date::
+
+ import datetime
+ from django.db.models import DateField, F
+ from django.db.models.functions import Cast
+
+ qs = MyModel.objects.annotate(
+ previous_day=Cast(
+ F("date_field") - datetime.timedelta(days=1),
+ output_field=DateField(),
+ )
+ )
+
+ Or on PostgreSQL only, use integer arithmetic to represent days::
+
+ from django.db.models import DateField, ExpressionWrapper, F
+
+ qs = MyModel.objects.annotate(
+ previous_day=ExpressionWrapper(
+ F("date_field") - 1, # Subtract 1 day as integer
+ output_field=DateField(),
+ )
+ )
+
``DateTimeField``
-----------------