Skip to content

Commit f43ebed

Browse files
committed
add support for ordering by F and OrderBy
1 parent 95c5372 commit f43ebed

File tree

3 files changed

+39
-36
lines changed

3 files changed

+39
-36
lines changed

django_mongodb/compiler.py

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
from django.core.exceptions import EmptyResultSet, FieldDoesNotExist, FullResultSet
1+
from django.core.exceptions import EmptyResultSet, FullResultSet
22
from django.db import DatabaseError, IntegrityError, NotSupportedError
33
from django.db.models import Count, Expression
44
from django.db.models.aggregates import Aggregate
5-
from django.db.models.constants import LOOKUP_SEP
5+
from django.db.models.expressions import OrderBy
66
from django.db.models.sql import compiler
77
from django.db.models.sql.constants import GET_ITERATOR_CHUNK_SIZE, MULTI
88
from django.utils.functional import cached_property
@@ -207,24 +207,23 @@ def _get_ordering(self):
207207
for order in ordering:
208208
if order == "?":
209209
raise NotSupportedError("Randomized ordering isn't supported by MongoDB.")
210-
211-
ascending = not order.startswith("-")
210+
if hasattr(order, "resolve_expression"):
211+
orderby = order if isinstance(order, OrderBy) else order.asc()
212+
orderby = orderby.resolve_expression(self.query, allow_joins=True, reuse=None)
213+
ascending = not orderby.descending
214+
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]
220+
column = orderby.expression.as_mql(self, self.connection)
221+
if isinstance(column, dict):
222+
raise NotSupportedError("order_by() expression not supported.")
223+
column = column.removeprefix("$")
224+
# If the query is reversed, ascending and descending are inverted.
212225
if not self.query.standard_ordering:
213226
ascending = not ascending
214-
215-
name = order.lstrip("+-")
216-
if name == "pk":
217-
name = opts.pk.name
218-
219-
if LOOKUP_SEP in order:
220-
orderby, _ = self.find_ordering_name(order, self.query.get_meta())[0]
221-
column = orderby.expression.as_mql(self, self.connection).removeprefix("$")
222-
else:
223-
try:
224-
column = opts.get_field(name).column
225-
except FieldDoesNotExist:
226-
# `name` is an annotation in $project.
227-
column = name
228227
column_ordering.append((column, ascending))
229228
return column_ordering
230229

django_mongodb/expressions.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
Col,
1010
CombinedExpression,
1111
ExpressionWrapper,
12+
F,
1213
NegatedExpression,
1314
Ref,
1415
Subquery,
@@ -61,6 +62,10 @@ def expression_wrapper(self, compiler, connection):
6162
return self.expression.as_mql(compiler, connection)
6263

6364

65+
def f(self, compiler, connection): # noqa: ARG001
66+
return f"${self.name}"
67+
68+
6469
def negated_expression(self, compiler, connection):
6570
return {"$not": expression_wrapper(self, compiler, connection)}
6671

@@ -102,6 +107,7 @@ def register_expressions():
102107
Col.as_mql = col
103108
CombinedExpression.as_mql = combined_expression
104109
ExpressionWrapper.as_mql = expression_wrapper
110+
F.as_mql = f
105111
NegatedExpression.as_mql = negated_expression
106112
Query.as_mql = query
107113
Ref.as_mql = ref

django_mongodb/features.py

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,37 +32,29 @@ class DatabaseFeatures(BaseDatabaseFeatures):
3232
"lookup.tests.LookupTests.test_exact_none_transform",
3333
# "Save with update_fields did not affect any rows."
3434
"basic.tests.SelectOnSaveTests.test_select_on_save_lying_update",
35-
# Lookup in order_by() not supported:
36-
# argument of type '<database function>' is not iterable
35+
# Order by constant not supported:
36+
# AttributeError: 'Field' object has no attribute 'model'
37+
"ordering.tests.OrderingTests.test_order_by_constant_value",
38+
"expressions.tests.NegatedExpressionTests.test_filter",
39+
# NotSupportedError: order_by() expression not supported.
3740
"db_functions.comparison.test_coalesce.CoalesceTests.test_ordering",
3841
"db_functions.tests.FunctionTests.test_nested_function_ordering",
3942
"db_functions.text.test_length.LengthTests.test_ordering",
4043
"db_functions.text.test_strindex.StrIndexTests.test_order_by",
41-
"expressions.tests.BasicExpressionsTests.test_order_by_exists",
42-
"expressions.tests.BasicExpressionsTests.test_order_by_multiline_sql",
4344
"expressions_case.tests.CaseExpressionTests.test_order_by_conditional_explicit",
4445
"lookup.tests.LookupQueryingTests.test_lookup_in_order_by",
45-
"ordering.tests.OrderingTests.test_default_ordering",
46-
"ordering.tests.OrderingTests.test_default_ordering_by_f_expression",
47-
"ordering.tests.OrderingTests.test_default_ordering_does_not_affect_group_by",
48-
"ordering.tests.OrderingTests.test_order_by_constant_value",
4946
"ordering.tests.OrderingTests.test_order_by_expr_query_reuse",
5047
"ordering.tests.OrderingTests.test_order_by_expression_ref",
51-
"ordering.tests.OrderingTests.test_order_by_f_expression",
48+
"ordering.tests.OrderingTests.test_ordering_select_related_collision",
49+
"update.tests.AdvancedTests.test_update_ordered_by_inline_m2m_annotation",
50+
"update.tests.AdvancedTests.test_update_ordered_by_m2m_annotation",
51+
"update.tests.AdvancedTests.test_update_ordered_by_m2m_annotation_desc",
52+
# Wrong results for ordering queries.
5253
"ordering.tests.OrderingTests.test_order_by_f_expression_duplicates",
53-
"ordering.tests.OrderingTests.test_order_by_fk_attname",
5454
"ordering.tests.OrderingTests.test_order_by_nulls_first",
5555
"ordering.tests.OrderingTests.test_order_by_nulls_last",
56-
"ordering.tests.OrderingTests.test_ordering_select_related_collision",
5756
"ordering.tests.OrderingTests.test_order_by_self_referential_fk",
58-
"ordering.tests.OrderingTests.test_orders_nulls_first_on_filtered_subquery",
5957
"ordering.tests.OrderingTests.test_related_ordering_duplicate_table_reference",
60-
"ordering.tests.OrderingTests.test_reverse_ordering_pure",
61-
"ordering.tests.OrderingTests.test_reverse_meta_ordering_pure",
62-
"ordering.tests.OrderingTests.test_reversed_ordering",
63-
"update.tests.AdvancedTests.test_update_ordered_by_inline_m2m_annotation",
64-
"update.tests.AdvancedTests.test_update_ordered_by_m2m_annotation",
65-
"update.tests.AdvancedTests.test_update_ordered_by_m2m_annotation_desc",
6658
# 'ManyToOneRel' object has no attribute 'column'
6759
"m2m_through.tests.M2mThroughTests.test_order_by_relational_field_through_model",
6860
"queries.tests.Queries4Tests.test_order_by_reverse_fk",
@@ -144,6 +136,8 @@ class DatabaseFeatures(BaseDatabaseFeatures):
144136
"queries.tests.Queries1Tests.test_tickets_2076_7256",
145137
# filter() on related model + update() doesn't work.
146138
"queries.tests.Queries5Tests.test_ticket9848",
139+
# ???
140+
"expressions.tests.BasicExpressionsTests.test_outerref",
147141
}
148142
# $bitAnd, #bitOr, and $bitXor are new in MongoDB 6.3.
149143
_django_test_expected_failures_bitwise = {
@@ -327,6 +321,7 @@ def django_test_expected_failures(self):
327321
"expressions.tests.BasicExpressionsTests.test_boolean_expression_in_Q",
328322
"expressions.tests.BasicExpressionsTests.test_case_in_filter_if_boolean_output_field",
329323
"expressions.tests.BasicExpressionsTests.test_exists_in_filter",
324+
"expressions.tests.BasicExpressionsTests.test_order_by_exists",
330325
"expressions.tests.BasicExpressionsTests.test_subquery",
331326
"expressions.tests.ExistsTests.test_filter_by_empty_exists",
332327
"expressions.tests.ExistsTests.test_negated_empty_exists",
@@ -445,6 +440,7 @@ def django_test_expected_failures(self):
445440
"expressions.tests.FieldTransformTests.test_month_aggregation",
446441
"expressions_case.tests.CaseDocumentationExamples.test_conditional_aggregation_example",
447442
"model_fields.test_jsonfield.TestQuerying.test_ordering_grouping_by_count",
443+
"ordering.tests.OrderingTests.test_default_ordering_does_not_affect_group_by",
448444
"queries.tests.Queries1Tests.test_ticket_20250",
449445
"queries.tests.ValuesQuerysetTests.test_named_values_list_expression_with_default_alias",
450446
},
@@ -478,6 +474,7 @@ def django_test_expected_failures(self):
478474
},
479475
"QuerySet.distinct() is not supported.": {
480476
"lookup.tests.LookupTests.test_lookup_collision_distinct",
477+
"ordering.tests.OrderingTests.test_orders_nulls_first_on_filtered_subquery",
481478
"queries.tests.ExcludeTest17600.test_exclude_plain_distinct",
482479
"queries.tests.ExcludeTest17600.test_exclude_with_q_is_equal_to_plain_exclude",
483480
"queries.tests.ExcludeTest17600.test_exclude_with_q_object_distinct",
@@ -536,6 +533,7 @@ def django_test_expected_failures(self):
536533
"delete_regress.tests.DeleteLockingTest.test_concurrent_delete",
537534
"expressions.tests.BasicExpressionsTests.test_annotate_values_filter",
538535
"expressions.tests.BasicExpressionsTests.test_filtering_on_rawsql_that_is_boolean",
536+
"expressions.tests.BasicExpressionsTests.test_order_by_multiline_sql",
539537
"model_fields.test_jsonfield.TestQuerying.test_key_sql_injection_escape",
540538
"model_fields.test_jsonfield.TestQuerying.test_key_transform_raw_expression",
541539
"model_fields.test_jsonfield.TestQuerying.test_nested_key_transform_raw_expression",

0 commit comments

Comments
 (0)