|
6 | 6 | from django.db import DatabaseError, IntegrityError, NotSupportedError
|
7 | 7 | from django.db.models import Count, Expression
|
8 | 8 | from django.db.models.aggregates import Aggregate, Variance
|
9 |
| -from django.db.models.expressions import Col, Ref, Value |
| 9 | +from django.db.models.expressions import Case, Col, Ref, Value, When |
10 | 10 | from django.db.models.functions.comparison import Coalesce
|
11 | 11 | from django.db.models.functions.math import Power
|
| 12 | +from django.db.models.lookups import IsNull |
12 | 13 | from django.db.models.sql import compiler
|
13 | 14 | from django.db.models.sql.constants import GET_ITERATOR_CHUNK_SIZE, MULTI, SINGLE
|
14 | 15 | from django.utils.functional import cached_property
|
@@ -351,8 +352,13 @@ def build_query(self, columns=None):
|
351 | 352 | ordering_fields, sort_ordering, extra_fields = self._get_ordering()
|
352 | 353 | query.project_fields = self.get_project_fields(columns, ordering_fields)
|
353 | 354 | query.ordering = sort_ordering
|
354 |
| - if extra_fields and columns is None: |
355 |
| - query.extra_fields = self.get_project_fields(extra_fields) |
| 355 | + # extra_fields may contain references to other columns or expressions. |
| 356 | + if columns is None: |
| 357 | + extra_fields += ordering_fields |
| 358 | + if extra_fields: |
| 359 | + query.extra_fields = { |
| 360 | + field_name: expr.as_mql(self, self.connection) for field_name, expr in extra_fields |
| 361 | + } |
356 | 362 | where = self.get_where()
|
357 | 363 | try:
|
358 | 364 | expr = where.as_mql(self, self.connection) if where else {}
|
@@ -465,13 +471,24 @@ def _get_ordering(self):
|
465 | 471 | idx = itertools.count(start=1)
|
466 | 472 | for order in self.order_by_expressions or []:
|
467 | 473 | if isinstance(order.expression, Ref):
|
| 474 | + # TODO change? |
| 475 | + # field_name = order.expression.as_mql(self, self.connection).removeprefix("$") |
| 476 | + # extra_fields[field_name] = order.expression |
468 | 477 | field_name = order.expression.refs
|
469 | 478 | elif isinstance(order.expression, Col):
|
470 | 479 | field_name = order.expression.as_mql(self, self.connection).removeprefix("$")
|
| 480 | + fields[field_name] = order.expression |
471 | 481 | else:
|
472 | 482 | # The expression must be added to extra_fields with an alias.
|
473 | 483 | field_name = f"__order{next(idx)}"
|
474 | 484 | extra_fields[field_name] = order.expression
|
| 485 | + # If the expression is ordered by NULLS FIRST or NULLS LAST, |
| 486 | + # add a field for sorting that's 1 if null or 0 if not. |
| 487 | + if order.nulls_first or order.nulls_last: |
| 488 | + null_fieldname = f"__order{next(idx)}" |
| 489 | + condition = When(IsNull(order.expression, True), then=Value(1)) |
| 490 | + extra_fields[null_fieldname] = Case(condition, default=Value(0)) |
| 491 | + sort_ordering[null_fieldname] = DESCENDING if order.nulls_first else ASCENDING |
475 | 492 | fields[field_name] = order.expression
|
476 | 493 | sort_ordering[field_name] = DESCENDING if order.descending else ASCENDING
|
477 | 494 | return tuple(fields.items()), sort_ordering, tuple(extra_fields.items())
|
|
0 commit comments