From 7cf1c22d4dfdd46f2082cfc55b714b68c4fd2de3 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Mon, 9 Feb 2026 15:09:56 -0500 Subject: [PATCH 1/2] Refs #36644 -- Documented no pk ordering in first()/last() after empty order_by(). --- docs/ref/models/querysets.txt | 11 +++++++++-- docs/releases/6.1.txt | 4 ++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index 890395f6de21..c819015b2517 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -2774,8 +2774,9 @@ the direction is changed. *Asynchronous version*: ``afirst()`` Returns the first object matched by the queryset, or ``None`` if there -is no matching object. If the ``QuerySet`` has no ordering defined, then the -queryset is automatically ordered by the primary key. This can affect +is no matching object. If the ``QuerySet`` has no ordering defined (and has not +had ordering forcibly cleared by calling :meth:`order_by` with no arguments), +then the queryset is automatically ordered by the primary key. This can affect aggregation results as described in :ref:`aggregation-ordering-interaction`. Example:: @@ -2790,6 +2791,12 @@ equivalent to the above example:: except IndexError: p = None +.. versionchanged:: 6.1 + + ``first()`` and :meth:`last` no longer order by the primary key when + ordering has been forcibly cleared by calling :meth:`order_by` with no + arguments. + ``last()`` ~~~~~~~~~~ diff --git a/docs/releases/6.1.txt b/docs/releases/6.1.txt index ca9ba16e84e0..a26b2be07f00 100644 --- a/docs/releases/6.1.txt +++ b/docs/releases/6.1.txt @@ -422,6 +422,10 @@ Miscellaneous ``null``, to match the behavior of :lookup:`exact=None ` on key transforms. Previously, it was interpreted as an :lookup:`isnull` lookup. +* :meth:`~.QuerySet.first` and :meth:`~.QuerySet.last` no longer order by the + primary key when a ``QuerySet``'s ordering has been forcibly cleared by + calling :meth:`~.QuerySet.order_by` with no arguments. + .. _deprecated-features-6.1: Features deprecated in 6.1 From 226ca7b5ce283e1f891dbbbefb0afaebdaa09f28 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Tue, 23 Dec 2025 19:34:32 -0500 Subject: [PATCH 2/2] Added DatabaseFeatures.supports_inspectdb. Needed by MongoDB. --- django/core/management/commands/inspectdb.py | 4 ++-- django/db/backends/base/features.py | 3 +++ docs/releases/6.1.txt | 3 +++ tests/gis_tests/inspectapp/tests.py | 1 + tests/inspectdb/tests.py | 22 ++++++++++++++++++-- 5 files changed, 29 insertions(+), 4 deletions(-) diff --git a/django/core/management/commands/inspectdb.py b/django/core/management/commands/inspectdb.py index 06adfe39ed31..8bd6c4bc0c21 100644 --- a/django/core/management/commands/inspectdb.py +++ b/django/core/management/commands/inspectdb.py @@ -44,10 +44,10 @@ def add_arguments(self, parser): ) def handle(self, **options): - try: + if connections[options["database"]].features.supports_inspectdb: for line in self.handle_inspection(options): self.stdout.write(line) - except NotImplementedError: + else: raise CommandError( "Database inspection isn't supported for the currently selected " "database backend." diff --git a/django/db/backends/base/features.py b/django/db/backends/base/features.py index 2031beaf8a0e..e8fa82aa21cc 100644 --- a/django/db/backends/base/features.py +++ b/django/db/backends/base/features.py @@ -400,6 +400,9 @@ class BaseDatabaseFeatures: supports_on_delete_db_default = True supports_on_delete_db_null = True + # Does the backend support the inspectdb management command? + supports_inspectdb = True + # Collation names for use by the Django test suite. test_collations = { "ci": None, # Case-insensitive. diff --git a/docs/releases/6.1.txt b/docs/releases/6.1.txt index a26b2be07f00..0d3598254381 100644 --- a/docs/releases/6.1.txt +++ b/docs/releases/6.1.txt @@ -367,6 +367,9 @@ backends. ``db_on_delete``) as values. ``db_on_delete`` is one of the database-level delete options e.g. :attr:`~django.db.models.DB_CASCADE`. +* Set the new ``DatabaseFeatures.supports_inspectdb`` attribute to ``False`` + if the management command isn't supported. + :mod:`django.contrib.gis` ------------------------- diff --git a/tests/gis_tests/inspectapp/tests.py b/tests/gis_tests/inspectapp/tests.py index 43227d232d97..fd6fec32d991 100644 --- a/tests/gis_tests/inspectapp/tests.py +++ b/tests/gis_tests/inspectapp/tests.py @@ -14,6 +14,7 @@ from .models import AllOGRFields +@skipUnlessDBFeature("supports_inspectdb") class InspectDbTests(TestCase): def test_geom_columns(self): """ diff --git a/tests/inspectdb/tests.py b/tests/inspectdb/tests.py index 8175c52e4ec9..b677af23df80 100644 --- a/tests/inspectdb/tests.py +++ b/tests/inspectdb/tests.py @@ -2,11 +2,16 @@ from io import StringIO from unittest import mock, skipUnless -from django.core.management import call_command +from django.core.management import CommandError, call_command from django.core.management.commands import inspectdb from django.db import connection from django.db.backends.base.introspection import TableInfo -from django.test import TestCase, TransactionTestCase, skipUnlessDBFeature +from django.test import ( + TestCase, + TransactionTestCase, + skipIfDBFeature, + skipUnlessDBFeature, +) from .models import PeopleMoreData, test_collation @@ -40,6 +45,7 @@ def cursor_execute(*queries): return results +@skipUnlessDBFeature("supports_inspectdb") class InspectDBTestCase(TestCase): unique_re = re.compile(r".*unique_together = \((.+),\).*") @@ -519,6 +525,7 @@ def test_same_relations(self): ) +@skipUnlessDBFeature("supports_inspectdb") class InspectDBTransactionalTests(TransactionTestCase): available_apps = ["inspectdb"] @@ -671,3 +678,14 @@ def test_composite_primary_key_not_unique_together(self): out = StringIO() call_command("inspectdb", "inspectdb_compositepkmodel", stdout=out) self.assertNotIn("unique_together", out.getvalue()) + + +@skipIfDBFeature("supports_inspectdb") +class InspectDBNotSupportedTests(TestCase): + def test_not_supported(self): + msg = ( + "Database inspection isn't supported for the currently selected " + "database backend." + ) + with self.assertRaisesMessage(CommandError, msg): + call_command("inspectdb")