#36644: Enable using an empty order_by() to disable implicit primary key ordering
in first()
-------------------------------------+-------------------------------------
Reporter: Lily | Owner: Mridul
Type: New feature | Status: assigned
Component: Database layer | Version: dev
(models, ORM) |
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Jacob Walls):
* cc: Jake Howard (added)
Comment:
For folks checking the forum thread, when Tom Carrick summarized the
options discussed [
https://github.com/django/new-
features/issues/71#issuecomment-
3325381421 here], this was "option 2", to
which I did not see specific objections afterward.
----
> This will interact with default orderings (`Meta.ordering`), which will
need some careful handling.
I think that looks like this:
{{{#!diff
diff --git a/django/db/models/query.py b/django/db/models/query.py
index b404fd1875..4d1218ea89 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -1135,9 +1135,13 @@ class QuerySet(AltersData):
def first(self):
"""Return the first object of a query or None if no match is
found."""
+ # RemovedInDjango70Warning: replace with:
+ # if self.ordered or not self.query.default_ordering:
if self.ordered:
queryset = self
else:
+ if not self.query.default_ordering:
+ warnings.warn(..., RemovedInDjango70Warning,
skip_file_prefixes=django_file_prefixes())
self._check_ordering_first_last_queryset_aggregation(method="first")
queryset = self.order_by("pk")
for obj in queryset[:1]:
@@ -1148,9 +1152,13 @@ class QuerySet(AltersData):
def last(self):
"""Return the last object of a query or None if no match is
found."""
+ # RemovedInDjango70Warning: replace with:
+ # if self.ordered or not self.query.default_ordering:
if self.ordered:
queryset = self.reverse()
else:
+ if not self.query.default_ordering:
+ warnings.warn(..., RemovedInDjango70Warning,
skip_file_prefixes=django_file_prefixes())
self._check_ordering_first_last_queryset_aggregation(method="last")
queryset = self.order_by("-pk")
for obj in queryset[:1]:
diff --git a/tests/queries/test_qs_combinators.py
b/tests/queries/test_qs_combinators.py
index e329d0c4f0..79cfb78dd7 100644
--- a/tests/queries/test_qs_combinators.py
+++ b/tests/queries/test_qs_combinators.py
@@ -416,7 +416,7 @@ class QuerySetSetOperationTests(TestCase):
base_qs = Author.objects.order_by()
qs1 = base_qs.filter(name="a1")
qs2 = base_qs.filter(name="a2")
- self.assertEqual(qs1.union(qs2).first(), a1)
+ self.assertEqual(qs1.union(qs2).order_by("pk").first(), a1)
def test_union_multiple_models_with_values_list_and_order(self):
reserved_name = ReservedName.objects.create(name="rn1", order=0)
}}}
I think this will need a deprecation (notice the edited test). We will be
enforcing some more explicitness for users who must to clear an ordering
for the sake of doing a union but then need to add it back to retain the
prior behavior of `first()`, but I think that trade-off is worth it.
--
Ticket URL: <
https://code.djangoproject.com/ticket/36644#comment:3>