From 08aa1b5ac8fd95d165aad749f3ce29de4aa1be0d Mon Sep 17 00:00:00 2001 From: Rodrigo Vieira Date: Tue, 31 Mar 2026 20:31:57 -0300 Subject: [PATCH 1/2] Refs #37004 -- Added coverage for BaseModelFormSet.get_queryset() fallback ordering. --- tests/model_formsets/tests.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/model_formsets/tests.py b/tests/model_formsets/tests.py index 02c928cba230..40f206277b85 100644 --- a/tests/model_formsets/tests.py +++ b/tests/model_formsets/tests.py @@ -1773,6 +1773,17 @@ def test_model_formset_with_initial_queryset(self): formset = FormSet(initial=[{"authors": Author.objects.all()}], data=data) self.assertFalse(formset.extra_forms[0].has_changed()) + def test_get_queryset_falls_back_to_pk_when_no_ordering_defined(self): + # Product has no Meta.ordering, so the default queryset is not + # totally ordered. + self.assertIs(Product._default_manager.get_queryset().totally_ordered, False) + + ProductFormSet = modelformset_factory(Product, fields="__all__") + formset = ProductFormSet() + + queryset = formset.get_queryset() + self.assertEqual(queryset.query.order_by, ("id",)) + def test_prevent_duplicates_from_with_the_same_formset(self): FormSet = modelformset_factory(Product, fields="__all__", extra=2) data = { From 856c915326768962806705ca7733e4abbb8f794f Mon Sep 17 00:00:00 2001 From: Rodrigo Vieira Date: Tue, 31 Mar 2026 20:34:56 -0300 Subject: [PATCH 2/2] Fixed #37004 -- Used QuerySet.totally_ordered in BaseModelFormSet.get_queryset() for stable ordering. --- AUTHORS | 1 + django/forms/models.py | 5 +++-- tests/model_formsets/tests.py | 31 ++++++++++++++++++++++++++++++- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index bf5bf03f1249..486a50f0e285 100644 --- a/AUTHORS +++ b/AUTHORS @@ -916,6 +916,7 @@ answer newbie questions, and generally made Django that much better: Rob Hudson Rob Nguyen Robin Munn + Rodrigo Bastos Vieira Rodrigo Pinheiro Marques de Araújo Roel Delos Reyes Rohith P R diff --git a/django/forms/models.py b/django/forms/models.py index 104369c0b03d..a53f1199955f 100644 --- a/django/forms/models.py +++ b/django/forms/models.py @@ -776,8 +776,9 @@ def get_queryset(self): # If the queryset isn't already ordered we need to add an # artificial ordering here to make sure that all formsets # constructed from this queryset have the same form order. - if not qs.ordered: - qs = qs.order_by(self.model._meta.pk.name) + if not qs.totally_ordered: + current_ordering = qs.query.order_by or qs.model._meta.ordering or [] + qs = qs.order_by(*current_ordering, "pk") # Removed queryset limiting here. As per discussion re: #13023 # on django-dev, max_num should not prevent existing diff --git a/tests/model_formsets/tests.py b/tests/model_formsets/tests.py index 40f206277b85..9436642573fe 100644 --- a/tests/model_formsets/tests.py +++ b/tests/model_formsets/tests.py @@ -1782,7 +1782,36 @@ def test_get_queryset_falls_back_to_pk_when_no_ordering_defined(self): formset = ProductFormSet() queryset = formset.get_queryset() - self.assertEqual(queryset.query.order_by, ("id",)) + self.assertEqual(queryset.query.order_by, ("pk",)) + + def test_get_queryset_appends_pk_to_explicit_queryset_ordering(self): + # Author.name is non-unique, so order_by("name") is not totally + # ordered. + AuthorFormSet = modelformset_factory(Author, fields="__all__") + formset = AuthorFormSet(queryset=Author.objects.order_by("name")) + + queryset = formset.get_queryset() + self.assertEqual(queryset.query.order_by, ("name", "pk")) + + def test_get_queryset_appends_pk_to_meta_ordering(self): + # Author has Meta.ordering = ("name",) which is not deterministic + # by itself. + AuthorFormSet = modelformset_factory(Author, fields="__all__") + formset = AuthorFormSet() + + queryset = formset.get_queryset() + self.assertEqual(queryset.query.order_by, ("name", "pk")) + + def test_get_queryset_unchanged_when_already_totally_ordered(self): + # Ordering by pk is already totally ordered; pk must not be + # appended again. + AuthorFormSet = modelformset_factory(Author, fields="__all__") + formset = AuthorFormSet(queryset=Author.objects.order_by("pk")) + + queryset = formset.get_queryset() + # Must be exactly ("pk",), not ("pk", "pk"). + self.assertEqual(queryset.query.order_by, ("pk",)) + self.assertIs(queryset.totally_ordered, True) def test_prevent_duplicates_from_with_the_same_formset(self): FormSet = modelformset_factory(Product, fields="__all__", extra=2)