summaryrefslogtreecommitdiff
path: root/django/db/models/query.py
diff options
context:
space:
mode:
authorVIZZARD-X <vigneshanandmay13@gmail.com>2026-01-10 02:26:37 +0530
committerJacob Walls <jacobtylerwalls@gmail.com>2026-02-13 16:58:36 -0500
commit08b4dfc5734f5d2fce685eabcd65385a6656db2f (patch)
tree468d1e7db12407c049e892181eb3ac3866b3ab05 /django/db/models/query.py
parent3dea5fed077e33c7d8bca4b5eeade5420cb05d27 (diff)
Fixed #36857 -- Added QuerySet.totally_ordered property.
Thanks Simon Charette for the idea.
Diffstat (limited to 'django/db/models/query.py')
-rw-r--r--django/db/models/query.py76
1 files changed, 75 insertions, 1 deletions
diff --git a/django/db/models/query.py b/django/db/models/query.py
index 76d0f449a6..cbe77caea9 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -26,7 +26,7 @@ from django.db import (
from django.db.models import AutoField, DateField, DateTimeField, Field, Max, sql
from django.db.models.constants import LOOKUP_SEP, OnConflict
from django.db.models.deletion import Collector
-from django.db.models.expressions import Case, DatabaseDefault, F, Value, When
+from django.db.models.expressions import Case, DatabaseDefault, F, OrderBy, Value, When
from django.db.models.fetch_modes import FETCH_ONE
from django.db.models.functions import Cast, Trunc
from django.db.models.query_utils import FilteredRelation, Q
@@ -1975,6 +1975,80 @@ class QuerySet(AltersData):
return False
@property
+ def totally_ordered(self):
+ """
+ Returns True if the QuerySet is ordered and the ordering is
+ deterministic. This requires that the ordering includes a field
+ (or set of fields) that is unique and non-nullable.
+
+ For queries involving a GROUP BY clause, the model's default
+ ordering is ignored. Ordering specified via .extra(order_by=...)
+ is also ignored.
+ """
+ if not self.ordered:
+ return False
+ ordering = self.query.order_by
+ if not ordering and self.query.default_ordering:
+ ordering = self.query.get_meta().ordering
+ if not ordering:
+ return False
+ opts = self.model._meta
+ pk_fields = {f.attname for f in opts.pk_fields}
+ ordering_fields = set()
+ for part in ordering:
+ # Search for single field providing a total ordering.
+ field_name = None
+ if isinstance(part, str):
+ field_name = part.lstrip("-")
+ elif isinstance(part, F):
+ field_name = part.name
+ elif isinstance(part, OrderBy) and isinstance(part.expression, F):
+ field_name = part.expression.name
+ if field_name:
+ if field_name == "pk":
+ return True
+ # Normalize attname references by using get_field().
+ try:
+ field = opts.get_field(field_name)
+ except exceptions.FieldDoesNotExist:
+ # Could be "?" for random ordering or a related field
+ # lookup. Skip this part of introspection for now.
+ continue
+ # Ordering by a related field name orders by the referenced
+ # model's ordering. Skip this part of introspection for now.
+ if field.remote_field and field_name == field.name:
+ continue
+ if field.attname in pk_fields and len(pk_fields) == 1:
+ return True
+ if field.unique and not field.null:
+ return True
+ ordering_fields.add(field.attname)
+
+ # Account for members of a CompositePrimaryKey.
+ if ordering_fields.issuperset(pk_fields):
+ return True
+ # No single total ordering field, try unique_together and total
+ # unique constraints.
+ constraint_field_names = (
+ *opts.unique_together,
+ *(constraint.fields for constraint in opts.total_unique_constraints),
+ )
+ for field_names in constraint_field_names:
+ # Normalize attname references by using get_field().
+ try:
+ fields = [opts.get_field(field_name) for field_name in field_names]
+ except exceptions.FieldDoesNotExist:
+ continue
+ # Composite unique constraints containing a nullable column
+ # cannot ensure total ordering.
+ if any(field.null for field in fields):
+ continue
+ if ordering_fields.issuperset(field.attname for field in fields):
+ return True
+
+ return False
+
+ @property
def db(self):
"""Return the database used if this query is executed now."""
if self._for_write: