You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

mixins.py 2.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. from django.db.models.expressions import F, OrderBy
  2. class OrderableAggMixin:
  3. def __init__(self, expression, ordering=(), **extra):
  4. if not isinstance(ordering, (list, tuple)):
  5. ordering = [ordering]
  6. ordering = ordering or []
  7. # Transform minus sign prefixed strings into an OrderBy() expression.
  8. ordering = (
  9. (OrderBy(F(o[1:]), descending=True) if isinstance(o, str) and o[0] == '-' else o)
  10. for o in ordering
  11. )
  12. super().__init__(expression, **extra)
  13. self.ordering = self._parse_expressions(*ordering)
  14. def resolve_expression(self, *args, **kwargs):
  15. self.ordering = [expr.resolve_expression(*args, **kwargs) for expr in self.ordering]
  16. return super().resolve_expression(*args, **kwargs)
  17. def as_sql(self, compiler, connection):
  18. if self.ordering:
  19. ordering_params = []
  20. ordering_expr_sql = []
  21. for expr in self.ordering:
  22. expr_sql, expr_params = expr.as_sql(compiler, connection)
  23. ordering_expr_sql.append(expr_sql)
  24. ordering_params.extend(expr_params)
  25. sql, sql_params = super().as_sql(compiler, connection, ordering=(
  26. 'ORDER BY ' + ', '.join(ordering_expr_sql)
  27. ))
  28. return sql, sql_params + ordering_params
  29. return super().as_sql(compiler, connection, ordering='')
  30. def set_source_expressions(self, exprs):
  31. # Extract the ordering expressions because ORDER BY clause is handled
  32. # in a custom way.
  33. self.ordering = exprs[self._get_ordering_expressions_index():]
  34. return super().set_source_expressions(exprs[:self._get_ordering_expressions_index()])
  35. def get_source_expressions(self):
  36. return self.source_expressions + self.ordering
  37. def get_source_fields(self):
  38. # Filter out fields contributed by the ordering expressions as
  39. # these should not be used to determine which the return type of the
  40. # expression.
  41. return [
  42. e._output_field_or_none
  43. for e in self.get_source_expressions()[:self._get_ordering_expressions_index()]
  44. ]
  45. def _get_ordering_expressions_index(self):
  46. """Return the index at which the ordering expressions start."""
  47. source_expressions = self.get_source_expressions()
  48. return len(source_expressions) - len(self.ordering)