Skip to content

Commit 24087bd

Browse files
committed
Fix Model.save() updates and QuerySet.update() for $-prefixed strings
1 parent 7da0869 commit 24087bd

File tree

3 files changed

+50
-1
lines changed

3 files changed

+50
-1
lines changed

django_mongodb_backend/compiler.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,9 @@ def execute_sql(self, result_type):
883883
f"{field.__class__.__name__}."
884884
)
885885
prepared = field.get_db_prep_save(value, connection=self.connection)
886-
if hasattr(value, "as_mql"):
886+
if is_direct_value(value):
887+
prepared = {"$literal": prepared} if isinstance(value, str) else prepared
888+
else:
887889
prepared = prepared.as_mql(self, self.connection, as_expr=True)
888890
values[field.column] = prepared
889891
try:

django_mongodb_backend/test.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,18 @@ def assertAggregateQuery(self, query, expected_collection, expected_pipeline):
1919
eval(pipeline[:-1], {"SON": SON, "ObjectId": ObjectId, "Decimal128": Decimal128}, {}), # noqa: S307
2020
expected_pipeline,
2121
)
22+
23+
def assertUpdateQuery(self, query, expected_collection, expected_condition, expected_values):
24+
"""
25+
Assert that the logged query is equal to:
26+
db.{expected_collection}.update_many({expected_condition}, {expected_values})
27+
"""
28+
prefix, pipeline = query.split("(", 1)
29+
_, collection, operator = prefix.split(".")
30+
self.assertEqual(operator, "update_many")
31+
self.assertEqual(collection, expected_collection)
32+
condition, values = eval( # noqa: S307
33+
pipeline[:-1], {"SON": SON, "ObjectId": ObjectId, "Decimal128": Decimal128}, {}
34+
)
35+
self.assertEqual(condition, expected_condition)
36+
self.assertEqual(values, expected_values)

tests/basic_/tests.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,38 @@ def test_insert_dollar_prefix(self):
1414
self.assertEqual(obj.name, "$foobar")
1515

1616

17+
class UpdateDollarPrefixTests(MongoTestCaseMixin, TestCase):
18+
def test_update_dollar_prefix(self):
19+
"""$-prefixed values are correctly saved on update."""
20+
obj = Author.objects.create(name="foobar")
21+
obj.name = "$updated"
22+
with self.assertNumQueries(1) as ctx:
23+
obj.save()
24+
obj.refresh_from_db()
25+
self.assertEqual(obj.name, "$updated")
26+
self.assertUpdateQuery(
27+
ctx.captured_queries[0]["sql"],
28+
"basic__author",
29+
{"_id": obj.id},
30+
[{"$set": {"name": {"$literal": "$updated"}}}],
31+
)
32+
33+
def test_update_dollar_prefix_in_value_expression(self):
34+
"""$-prefixed Value() expressions are correctly handled on update."""
35+
obj = Author.objects.create(name="foobar")
36+
obj.name = Value("$updated")
37+
with self.assertNumQueries(1) as ctx:
38+
obj.save()
39+
obj.refresh_from_db()
40+
self.assertEqual(obj.name, "$updated")
41+
self.assertUpdateQuery(
42+
ctx.captured_queries[0]["sql"],
43+
"basic__author",
44+
{"_id": obj.id},
45+
[{"$set": {"name": {"$literal": "$updated"}}}],
46+
)
47+
48+
1749
class QueryDollarPrefixTests(MongoTestCaseMixin, TestCase):
1850
def test_query_injection(self):
1951
"""$-prefixed Value() expressions are correctly handled on update."""

0 commit comments

Comments
 (0)