Skip to content

Commit a6ca424

Browse files
committed
cosmetic edits and alphabetization
1 parent 6dcd40b commit a6ca424

File tree

5 files changed

+47
-51
lines changed

5 files changed

+47
-51
lines changed

django_mongodb/features.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ class DatabaseFeatures(BaseDatabaseFeatures):
4141
# tuple index out of range in process_rhs()
4242
"lookup.tests.LookupTests.test_exact_sliced_queryset_limit_one",
4343
"lookup.tests.LookupTests.test_exact_sliced_queryset_limit_one_offset",
44-
# Regex lookup doesn't work on json fields.
44+
# Regex lookup doesn't work on JSONField:
45+
# Unsupported conversion from array to string in $convert
4546
"model_fields.test_jsonfield.TestQuerying.test_icontains",
4647
# MongoDB gives the wrong result of log(number, base) when base is a
4748
# fractional Decimal: https://jira.mongodb.org/browse/SERVER-91223
@@ -58,7 +59,8 @@ class DatabaseFeatures(BaseDatabaseFeatures):
5859
"model_fields.test_jsonfield.TestQuerying.test_usage_in_subquery",
5960
# Length of null considered zero rather than null.
6061
"db_functions.text.test_length.LengthTests.test_basic",
61-
# Django-mongo does not differentiate well between joins and key transforms.
62+
# Key transforms are incorrectly treated as joins:
63+
# Ordering can't span tables on MongoDB (value_custom__a).
6264
"model_fields.test_jsonfield.TestQuerying.test_order_grouping_custom_decoder",
6365
"model_fields.test_jsonfield.TestQuerying.test_ordering_by_transform",
6466
"model_fields.test_jsonfield.TestQuerying.test_ordering_grouping_by_key_transform",
@@ -334,8 +336,8 @@ def django_test_expected_failures(self):
334336
"lookup.tests.LookupTests.test_lookup_collision",
335337
"lookup.tests.LookupTests.test_lookup_rhs",
336338
"lookup.tests.LookupTests.test_isnull_non_boolean_value",
337-
"model_fields.test_manytomanyfield.ManyToManyFieldDBTests.test_value_from_object_instance_with_pk",
338339
"model_fields.test_jsonfield.TestQuerying.test_join_key_transform_annotation_expression",
340+
"model_fields.test_manytomanyfield.ManyToManyFieldDBTests.test_value_from_object_instance_with_pk",
339341
"model_fields.test_uuid.TestAsPrimaryKey.test_two_level_foreign_keys",
340342
"timezones.tests.LegacyDatabaseTests.test_query_annotation",
341343
"timezones.tests.NewDatabaseTests.test_query_annotation",
@@ -354,16 +356,16 @@ def django_test_expected_failures(self):
354356
},
355357
"Test executes raw SQL.": {
356358
"annotations.tests.NonAggregateAnnotationTestCase.test_raw_sql_with_inherited_field",
359+
"model_fields.test_jsonfield.TestQuerying.test_key_sql_injection_escape",
360+
"model_fields.test_jsonfield.TestQuerying.test_key_transform_raw_expression",
361+
"model_fields.test_jsonfield.TestQuerying.test_nested_key_transform_raw_expression",
357362
"timezones.tests.LegacyDatabaseTests.test_cursor_execute_accepts_naive_datetime",
358363
"timezones.tests.LegacyDatabaseTests.test_cursor_execute_returns_naive_datetime",
359364
"timezones.tests.LegacyDatabaseTests.test_raw_sql",
360365
"timezones.tests.NewDatabaseTests.test_cursor_execute_accepts_naive_datetime",
361366
"timezones.tests.NewDatabaseTests.test_cursor_execute_returns_naive_datetime",
362367
"timezones.tests.NewDatabaseTests.test_cursor_explicit_time_zone",
363368
"timezones.tests.NewDatabaseTests.test_raw_sql",
364-
"model_fields.test_jsonfield.TestQuerying.test_key_transform_raw_expression",
365-
"model_fields.test_jsonfield.TestQuerying.test_key_sql_injection_escape",
366-
"model_fields.test_jsonfield.TestQuerying.test_nested_key_transform_raw_expression",
367369
},
368370
"Bilateral transform not implemented.": {
369371
"db_functions.tests.FunctionTests.test_func_transform_bilateral",
@@ -415,7 +417,7 @@ def django_test_expected_failures(self):
415417
"db_functions.comparison.test_cast.CastTests.test_cast_from_python_to_datetime",
416418
"db_functions.comparison.test_cast.CastTests.test_cast_to_duration",
417419
},
418-
"Mongodb's Null behaviour is different from sql's": {
420+
"MongoDB's null behavior is different from SQL's.": {
419421
"model_fields.test_jsonfield.TestQuerying.test_expression_wrapper_key_transform",
420422
"model_fields.test_jsonfield.TestSaveLoad.test_json_null_different_from_sql_null",
421423
"model_fields.test_jsonfield.TestQuerying.test_lookup_exclude",

django_mongodb/fields/__init__.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
from .auto import MongoAutoField
22
from .json import register_json_field
33

4+
__all__ = ["register_fields", "MongoAutoField"]
5+
46

57
def register_fields():
68
register_json_field()
7-
8-
9-
__all__ = ["MongoAutoField", "register_fields"]

django_mongodb/fields/json.py

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
KeyTransformNumericLookupMixin,
1414
)
1515

16-
from django_mongodb.lookups import builtin_lookup
17-
from django_mongodb.query_utils import process_lhs, process_rhs
16+
from ..lookups import builtin_lookup
17+
from ..query_utils import process_lhs, process_rhs
1818

1919

2020
def contained_by(self, compiler, connection): # noqa: ARG001
@@ -50,11 +50,8 @@ def has_key_lookup(self, compiler, connection):
5050
_process_rhs = JSONExact.process_rhs
5151

5252

53-
def json_process_rhs(self, compiler, connection):
54-
"""
55-
Django does some transformation over the parameters if it is null.
56-
to avoid ambiguity, we keep using the parent process_rhs.
57-
"""
53+
def json_exact_process_rhs(self, compiler, connection):
54+
"""Skip JSONExact.process_rhs()'s conversion of None to "null"."""
5855
return (
5956
super(JSONExact, self).process_rhs(compiler, connection)
6057
if connection.vendor == "mongodb"
@@ -100,22 +97,19 @@ def key_transform_in(self, compiler, connection):
10097
return {"$and": [expr, type_in]}
10198

10299

103-
def _key_transform_root(self, compiler, connection):
104-
previous = self.lhs
105-
while isinstance(previous, KeyTransform):
106-
previous = previous.lhs
107-
return previous.as_mql(compiler, connection)
108-
109-
110-
def key_transform_isnull(self, compiler, connection):
100+
def key_transform_is_null(self, compiler, connection):
111101
"""
112102
The KeyTransformIsNull lookup borrows the logic from HasKey for isnull=False.
113-
If isnull=True, the query should only match objects that do not have the key.
114-
# https://code.djangoproject.com/ticket/32252
103+
If isnull=True, the query should only match objects that don't have the key.
104+
https://code.djangoproject.com/ticket/32252
115105
"""
116106
lhs_mql = process_lhs(self, compiler, connection)
117107
rhs_mql = process_rhs(self, compiler, connection)
118-
root_column = _key_transform_root(self, compiler, connection)
108+
# Get the root column.
109+
previous = self.lhs
110+
while isinstance(previous, KeyTransform):
111+
previous = previous.lhs
112+
root_column = previous.as_mql(compiler, connection)
119113
type_in = _expr_type_in(lhs_mql, ["missing"])
120114
result = {"$or": [type_in, {"$eq": [root_column, None]}]}
121115
if not rhs_mql:
@@ -137,8 +131,8 @@ def register_json_field():
137131
HasKey.mongo_operator = None
138132
HasKeyLookup.as_mql = has_key_lookup
139133
HasKeys.mongo_operator = "$and"
140-
JSONExact.process_rhs = json_process_rhs
134+
JSONExact.process_rhs = json_exact_process_rhs
141135
KeyTransform.as_mql = key_transform
142136
KeyTransformIn.as_mql = key_transform_in
143-
KeyTransformIsNull.as_mql = key_transform_isnull
137+
KeyTransformIsNull.as_mql = key_transform_is_null
144138
KeyTransformNumericLookupMixin.as_mql = key_transform_numeric_lookup_mixin

django_mongodb/functions.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,11 @@ def cast(self, compiler, connection):
6262
lhs_mql = process_lhs(self, compiler, connection)[0]
6363
if max_length := self.output_field.max_length:
6464
lhs_mql = {"$substrCP": [lhs_mql, 0, max_length]}
65-
66-
# In case of "object", we skip the conversion as it doesn't need to be transformed
67-
# for interpretation by JSONField. JSONField can handle various types,
68-
# including int, object, or array.
65+
# Skip the conversion for "object" as it doesn't need to be transformed for
66+
# interpretation by JSONField, which can handle types including int,
67+
# object, or array.
6968
if output_type != "object":
7069
lhs_mql = {"$convert": {"input": lhs_mql, "to": output_type}}
71-
7270
if decimal_places := getattr(self.output_field, "decimal_places", None):
7371
lhs_mql = {"$trunc": [lhs_mql, decimal_places]}
7472
return lhs_mql

django_mongodb/lookups.py

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,22 @@ def builtin_lookup(self, compiler, connection):
1717
return connection.mongo_operators[self.lookup_name](lhs_mql, value)
1818

1919

20+
_field_resolve_expression_parameter = FieldGetDbPrepValueIterableMixin.resolve_expression_parameter
21+
22+
23+
def field_resolve_expression_parameter(self, compiler, connection, sql, param):
24+
"""For MongoDB, this method must call as_mql() instead of as_sql()."""
25+
sql, sql_params = _field_resolve_expression_parameter(self, compiler, connection, sql, param)
26+
if connection.vendor == "mongodb":
27+
params = [param]
28+
if hasattr(param, "resolve_expression"):
29+
param = param.resolve_expression(compiler.query)
30+
if hasattr(param, "as_mql"):
31+
params = [param.as_mql(compiler, connection)]
32+
return sql, params
33+
return sql, sql_params
34+
35+
2036
def in_(self, compiler, connection):
2137
if isinstance(self.lhs, MultiColSource):
2238
raise NotImplementedError("MultiColSource is not supported.")
@@ -52,24 +68,11 @@ def uuid_text_mixin(self, compiler, connection): # noqa: ARG001
5268
raise NotSupportedError("Pattern lookups on UUIDField are not supported.")
5369

5470

55-
_resolve_expression_parameter = FieldGetDbPrepValueIterableMixin.resolve_expression_parameter
56-
57-
58-
def resolve_expression_parameter(self, compiler, connection, sql, param):
59-
sql, sql_params = _resolve_expression_parameter(self, compiler, connection, sql, param)
60-
if connection.vendor == "mongodb":
61-
params = [param]
62-
if hasattr(param, "resolve_expression"):
63-
param = param.resolve_expression(compiler.query)
64-
if hasattr(param, "as_mql"):
65-
params = [param.as_mql(compiler, connection)]
66-
return sql, params
67-
return sql, sql_params
68-
69-
7071
def register_lookups():
7172
BuiltinLookup.as_mql = builtin_lookup
72-
FieldGetDbPrepValueIterableMixin.resolve_expression_parameter = resolve_expression_parameter
73+
FieldGetDbPrepValueIterableMixin.resolve_expression_parameter = (
74+
field_resolve_expression_parameter
75+
)
7376
In.as_mql = RelatedIn.as_mql = in_
7477
IsNull.as_mql = is_null
7578
PatternLookup.prep_lookup_value_mongo = pattern_lookup_prep_lookup_value

0 commit comments

Comments
 (0)