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 02c928cba230..9436642573fe 100644
--- a/tests/model_formsets/tests.py
+++ b/tests/model_formsets/tests.py
@@ -1773,6 +1773,46 @@ 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, ("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)
data = {