Skip to content

Value.as_mql() doesn't call output_field.get_db_prep_save() #282

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
timgraham opened this issue Apr 6, 2025 · 0 comments
Open

Value.as_mql() doesn't call output_field.get_db_prep_save() #282

timgraham opened this issue Apr 6, 2025 · 0 comments

Comments

@timgraham
Copy link
Collaborator

Unlike Value.as_sql(), Value.as_mql() doesn't call output_field.get_db_prep_save(). This hasn't been a problem on any tests except for a new one in Django 5.2, model_fields.test_jsonfield.TestSaveLoad.test_bulk_update_custom_get_prep_value.

I'm not sure how needful this fix is, and since the patch I wrote breaks another test (perhaps revealing a server-side bug), I'm just going to leave it here for now.

commit 5c7433e1f6e94b89ef87b3ba505c491e1f8fe84e
Author: Tim Graham <[email protected]>
Date:   Fri Feb 21 20:01:42 2025 -0500

    fix model_fields.test_jsonfield.TestSaveLoad.test_bulk_update_custom_get_prep_value
    
    https://github.com/django/django/commit/9525135698bd4f97cf1431776ef52ae393dfb3c0

diff --git a/django_mongodb_backend/expressions.py b/django_mongodb_backend/expressions.py
index e5ef335..2bf7e0e 100644
--- a/django_mongodb_backend/expressions.py
+++ b/django_mongodb_backend/expressions.py
@@ -186,6 +186,12 @@ def when(self, compiler, connection):
 
 def value(self, compiler, connection):  # noqa: ARG001
     value = self.value
+    output_field = self._output_field_or_none
+    if output_field is not None:
+        if self.for_save:
+            value = output_field.get_db_prep_save(value, connection=connection)
+        else:
+            value = output_field.get_db_prep_value(value, connection=connection)
     if isinstance(value, int):
         # Wrap numbers in $literal to prevent ambiguity when Value appears in
         # $project.
diff --git a/django_mongodb_backend/features.py b/django_mongodb_backend/features.py
index 1317a72..bb709b1 100644
--- a/django_mongodb_backend/features.py
+++ b/django_mongodb_backend/features.py
@@ -98,6 +98,8 @@ class DatabaseFeatures(BaseDatabaseFeatures):
         "prefetch_related.tests.NestedPrefetchTests.test_nested_prefetch_is_not_overwritten_by_related_object",
         "prefetch_related.tests.NullableTest.test_prefetch_nullable",
         "prefetch_related.tests.Ticket19607Tests.test_bug",
+        # {'$project': {'name': Decimal128('1')} is broken? (gives None)
+        "expressions.tests.ValueTests.test_output_field_decimalfield",
     }
     # $bitAnd, #bitOr, and $bitXor are new in MongoDB 6.3.
     _django_test_expected_failures_bitwise = {
diff --git a/tests/expressions_/test_value.py b/tests/expressions_/test_value.py
index c57c2f0..ad131a7 100644
--- a/tests/expressions_/test_value.py
+++ b/tests/expressions_/test_value.py
@@ -3,6 +3,7 @@ import uuid
 from decimal import Decimal
 
 from bson import Decimal128
+from django.db import connection
 from django.db.models import Value
 from django.test import SimpleTestCase
 
@@ -10,34 +11,36 @@ from django.test import SimpleTestCase
 class ValueTests(SimpleTestCase):
     def test_date(self):
         self.assertEqual(
-            Value(datetime.date(2025, 1, 1)).as_mql(None, None),
+            Value(datetime.date(2025, 1, 1)).as_mql(None, connection),
             datetime.datetime(2025, 1, 1),
         )
 
     def test_datetime(self):
         self.assertEqual(
-            Value(datetime.datetime(2025, 1, 1, 9, 8, 7)).as_mql(None, None),
+            Value(datetime.datetime(2025, 1, 1, 9, 8, 7)).as_mql(None, connection),
             datetime.datetime(2025, 1, 1, 9, 8, 7),
         )
 
     def test_decimal(self):
-        self.assertEqual(Value(Decimal("1.0")).as_mql(None, None), Decimal128("1.0"))
+        self.assertEqual(Value(Decimal("1.0")).as_mql(None, connection), Decimal128("1.0"))
 
     def test_time(self):
         self.assertEqual(
-            Value(datetime.time(9, 8, 7)).as_mql(None, None),
+            Value(datetime.time(9, 8, 7)).as_mql(None, connection),
             datetime.datetime(1, 1, 1, 9, 8, 7),
         )
 
     def test_timedelta(self):
-        self.assertEqual(Value(datetime.timedelta(3600)).as_mql(None, None), 311040000000.0)
+        self.assertEqual(
+            Value(datetime.timedelta(3600)).as_mql(None, connection), {"$literal": 311040000000}
+        )
 
     def test_int(self):
-        self.assertEqual(Value(1).as_mql(None, None), {"$literal": 1})
+        self.assertEqual(Value(1).as_mql(None, connection), {"$literal": 1})
 
     def test_str(self):
-        self.assertEqual(Value("foo").as_mql(None, None), "foo")
+        self.assertEqual(Value("foo").as_mql(None, connection), "foo")
 
     def test_uuid(self):
         value = uuid.UUID(int=1)
-        self.assertEqual(Value(value).as_mql(None, None), "00000000000000000000000000000001")
+        self.assertEqual(Value(value).as_mql(None, connection), "00000000000000000000000000000001")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant