Skip to content

Commit 337b1a3

Browse files
committed
fixes
1 parent 3c1b1cb commit 337b1a3

File tree

5 files changed

+31
-28
lines changed

5 files changed

+31
-28
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ Migrations for 'admin':
121121
- `Subquery`, `Exists`, and using a `QuerySet` in `QuerySet.annotate()` aren't
122122
supported.
123123

124+
* Ordering a `QuerySet` by `nulls_first` or `nulls_last` isn't supported.
125+
124126
- `DateTimeField` doesn't support microsecond precision, and correspondingly,
125127
`DurationField` stores milliseconds rather than microseconds.
126128

django_mongodb/compiler.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from django.db.models.aggregates import Aggregate
55
from django.db.models.expressions import OrderBy
66
from django.db.models.sql import compiler
7-
from django.db.models.sql.constants import GET_ITERATOR_CHUNK_SIZE, MULTI
7+
from django.db.models.sql.constants import GET_ITERATOR_CHUNK_SIZE, MULTI, ORDER_DIR
88
from django.utils.functional import cached_property
99

1010
from .base import Cursor
@@ -199,32 +199,34 @@ def _get_ordering(self):
199199
if self.query.default_ordering
200200
else self.query.order_by
201201
)
202-
203202
if not ordering:
204203
return self.query.standard_ordering
205-
204+
default_order, _ = ORDER_DIR["ASC" if self.query.standard_ordering else "DESC"]
206205
column_ordering = []
206+
columns_seen = set()
207207
for order in ordering:
208208
if order == "?":
209209
raise NotSupportedError("Randomized ordering isn't supported by MongoDB.")
210210
if hasattr(order, "resolve_expression"):
211211
orderby = order if isinstance(order, OrderBy) else order.asc()
212212
orderby = orderby.resolve_expression(self.query, allow_joins=True, reuse=None)
213213
ascending = not orderby.descending
214+
# If the query is reversed, ascending and descending are inverted.
215+
if not self.query.standard_ordering:
216+
ascending = not ascending
214217
else:
215-
ascending = not order.startswith("-")
216-
name = order.lstrip("+-")
217-
if name == "pk":
218-
name = opts.pk.name
219-
orderby, _ = self.find_ordering_name(order, self.query.get_meta())[0]
218+
orderby, _ = self.find_ordering_name(
219+
order, self.query.get_meta(), default_order=default_order
220+
)[0]
221+
ascending = not orderby.descending
220222
column = orderby.expression.as_mql(self, self.connection)
221223
if isinstance(column, dict):
222224
raise NotSupportedError("order_by() expression not supported.")
223225
column = column.removeprefix("$")
224-
# If the query is reversed, ascending and descending are inverted.
225-
if not self.query.standard_ordering:
226-
ascending = not ascending
227-
column_ordering.append((column, ascending))
226+
# Don't add the same column twice.
227+
if column not in columns_seen:
228+
columns_seen.add(column)
229+
column_ordering.append((column, ascending))
228230
return column_ordering
229231

230232
@cached_property

django_mongodb/expressions.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
F,
1313
NegatedExpression,
1414
Ref,
15+
ResolvedOuterRef,
1516
Subquery,
1617
Value,
1718
When,
@@ -111,6 +112,7 @@ def register_expressions():
111112
NegatedExpression.as_mql = negated_expression
112113
Query.as_mql = query
113114
Ref.as_mql = ref
115+
ResolvedOuterRef.as_mql = ResolvedOuterRef.as_sql
114116
Subquery.as_mql = subquery
115117
When.as_mql = when
116118
Value.as_mql = value

django_mongodb/features.py

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,10 @@ class DatabaseFeatures(BaseDatabaseFeatures):
4747
"ordering.tests.OrderingTests.test_order_by_expr_query_reuse",
4848
"ordering.tests.OrderingTests.test_order_by_expression_ref",
4949
"ordering.tests.OrderingTests.test_ordering_select_related_collision",
50+
"queries.tests.Queries1Tests.test_order_by_related_field_transform",
5051
"update.tests.AdvancedTests.test_update_ordered_by_inline_m2m_annotation",
5152
"update.tests.AdvancedTests.test_update_ordered_by_m2m_annotation",
5253
"update.tests.AdvancedTests.test_update_ordered_by_m2m_annotation_desc",
53-
# Wrong results for ordering queries.
54-
"ordering.tests.OrderingTests.test_order_by_f_expression_duplicates",
55-
"ordering.tests.OrderingTests.test_order_by_nulls_first",
56-
"ordering.tests.OrderingTests.test_order_by_nulls_last",
57-
"ordering.tests.OrderingTests.test_order_by_self_referential_fk",
58-
"ordering.tests.OrderingTests.test_related_ordering_duplicate_table_reference",
5954
# pymongo: ValueError: update cannot be empty
6055
"update.tests.SimpleTest.test_empty_update_with_inheritance",
6156
"update.tests.SimpleTest.test_nonempty_update_with_inheritance",
@@ -127,15 +122,8 @@ class DatabaseFeatures(BaseDatabaseFeatures):
127122
# QuerySet.explain() not implemented:
128123
# https://github.com/mongodb-labs/django-mongodb/issues/28
129124
"queries.test_explain.ExplainUnsupportedTests.test_message",
130-
# Ordering by related transform not supported.
131-
"queries.tests.Queries1Tests.test_order_by_related_field_transform",
132-
# Ordering on a related field should use the remote model's default
133-
# ordering as a final step.
134-
"queries.tests.Queries1Tests.test_tickets_2076_7256",
135125
# filter() on related model + update() doesn't work.
136126
"queries.tests.Queries5Tests.test_ticket9848",
137-
# ???
138-
"expressions.tests.BasicExpressionsTests.test_outerref",
139127
}
140128
# $bitAnd, #bitOr, and $bitXor are new in MongoDB 6.3.
141129
_django_test_expected_failures_bitwise = {
@@ -472,7 +460,6 @@ def django_test_expected_failures(self):
472460
},
473461
"QuerySet.distinct() is not supported.": {
474462
"lookup.tests.LookupTests.test_lookup_collision_distinct",
475-
"ordering.tests.OrderingTests.test_orders_nulls_first_on_filtered_subquery",
476463
"queries.tests.ExcludeTest17600.test_exclude_plain_distinct",
477464
"queries.tests.ExcludeTest17600.test_exclude_with_q_is_equal_to_plain_exclude",
478465
"queries.tests.ExcludeTest17600.test_exclude_with_q_object_distinct",
@@ -516,6 +503,11 @@ def django_test_expected_failures(self):
516503
"queries.tests.ValuesQuerysetTests.test_named_values_list_without_fields",
517504
"select_related.tests.SelectRelatedTests.test_select_related_with_extra",
518505
},
506+
"Ordering a QuerySet by null_first/nulls_last is not supported on MongoDB.": {
507+
"ordering.tests.OrderingTests.test_order_by_nulls_first",
508+
"ordering.tests.OrderingTests.test_order_by_nulls_last",
509+
"ordering.tests.OrderingTests.test_orders_nulls_first_on_filtered_subquery",
510+
},
519511
"QuerySet.update() crash: Unrecognized expression '$count'": {
520512
"update.tests.AdvancedTests.test_update_annotated_multi_table_queryset",
521513
},

django_mongodb/operations.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66

77
from bson.decimal128 import Decimal128
88
from django.conf import settings
9-
from django.db import DataError
9+
from django.db import DataError, NotSupportedError
1010
from django.db.backends.base.operations import BaseDatabaseOperations
11-
from django.db.models.expressions import Combinable
11+
from django.db.models.expressions import Combinable, OrderBy
1212
from django.utils import timezone
1313
from django.utils.regex_helper import _lazy_re_compile
1414

@@ -140,6 +140,11 @@ def convert_uuidfield_value(self, value, expression, connection):
140140
value = uuid.UUID(value)
141141
return value
142142

143+
def check_expression_support(self, expression):
144+
if isinstance(expression, OrderBy) and (expression.nulls_first or expression.nulls_last):
145+
option = "null_first" if expression.nulls_first else "nulls_last"
146+
raise NotSupportedError(f"Ordering a QuerySet by {option} is not supported on MongoDB.")
147+
143148
def combine_expression(self, connector, sub_expressions):
144149
lhs, rhs = sub_expressions
145150
if connector == Combinable.BITLEFTSHIFT:

0 commit comments

Comments
 (0)