Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -916,6 +916,7 @@ answer newbie questions, and generally made Django that much better:
Rob Hudson <https://rob.cogit8.org/>
Rob Nguyen <tienrobertnguyenn@gmail.com>
Robin Munn <http://www.geekforgod.com/>
Rodrigo Bastos Vieira <rodrigo.vieira@gmail.com>
Rodrigo Pinheiro Marques de Araújo <fenrrir@gmail.com>
Roel Delos Reyes <https://roelzkie.dev>
Rohith P R <https://rohithpr.com>
Expand Down
5 changes: 3 additions & 2 deletions django/forms/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
40 changes: 40 additions & 0 deletions tests/model_formsets/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
Loading