From 7576781cc6718fae313faadd29288ad8c4313f3c Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Fri, 7 Aug 2026 15:54:22 +0300 Subject: [PATCH 1/2] Fixed #37255 -- Avoided relying on implicit SELECT ordering in tests. Many (~70) tests assert a specific row order for querysets whose SQL has no ORDER BY. SQL standard does not define that order. On PostgreSQL the rows usually arrive in physical storage order, which on a freshly loaded table matches insertion order -- so these assertions pass by coincidence rather than by contract. That breaks when rows are updated or deleted and the space is reused, or when the planner picks a different access path such as an index scan. A test like this can start failing with no change to Django or to the test itself. The patch made sure that most of these tests do not rely on unspecified unordered selection order. --- tests/admin_utils/models.py | 10 ++++-- tests/admin_views/test_autocomplete_view.py | 4 +-- tests/composite_pk/tests.py | 2 +- tests/defer/tests.py | 10 ++++-- tests/expressions/test_queryset_values.py | 2 +- tests/expressions/tests.py | 4 +-- tests/foreign_object/tests.py | 8 ++--- tests/generic_relations/tests.py | 3 +- tests/generic_relations_regress/tests.py | 4 +-- tests/known_related_objects/tests.py | 5 +-- tests/m2m_recursive/tests.py | 12 ++++--- tests/m2m_through/tests.py | 2 +- tests/many_to_one/tests.py | 2 +- tests/migrations/test_commands.py | 2 +- tests/model_fields/test_jsonfield.py | 9 +++--- tests/model_forms/models.py | 6 ++++ tests/model_formsets/models.py | 3 ++ tests/multiple_database/tests.py | 4 +-- tests/postgres_tests/test_array.py | 30 ++++++++--------- tests/postgres_tests/test_bulk_update.py | 4 +-- tests/postgres_tests/test_hstore.py | 36 ++++++++++----------- tests/postgres_tests/test_ranges.py | 22 ++++++------- tests/postgres_tests/test_search.py | 4 +-- tests/proxy_models/tests.py | 2 +- tests/queries/test_qs_combinators.py | 4 +-- tests/queries/tests.py | 16 ++++----- tests/raw_query/tests.py | 36 ++++++++++----------- 27 files changed, 130 insertions(+), 116 deletions(-) diff --git a/tests/admin_utils/models.py b/tests/admin_utils/models.py index 055ef07b7550..49119473b022 100644 --- a/tests/admin_utils/models.py +++ b/tests/admin_utils/models.py @@ -47,6 +47,9 @@ class Cascade(models.Model): num = models.PositiveSmallIntegerField() parent = models.ForeignKey("self", models.CASCADE, null=True) + class Meta: + ordering = ("num",) + @admin.display(ordering="num") def __str__(self): return str(self.num) @@ -56,12 +59,13 @@ class DBCascade(models.Model): num = models.PositiveSmallIntegerField() parent = models.ForeignKey("self", models.DB_CASCADE, null=True) - def __str__(self): - return str(self.num) - class Meta: + ordering = ("num",) required_db_features = {"supports_on_delete_db_cascade"} + def __str__(self): + return str(self.num) + class Event(models.Model): date = models.DateTimeField(auto_now_add=True) diff --git a/tests/admin_views/test_autocomplete_view.py b/tests/admin_views/test_autocomplete_view.py index c3ad4978f7fb..9f7ffc609fd4 100644 --- a/tests/admin_views/test_autocomplete_view.py +++ b/tests/admin_views/test_autocomplete_view.py @@ -342,7 +342,7 @@ class PKOrderingQuestionAdmin(QuestionAdmin): { "results": [ {"id": str(q.pk), "text": q.question} - for q in Question.objects.all()[:PAGINATOR_SIZE] + for q in Question.objects.order_by("pk")[:PAGINATOR_SIZE] ], "pagination": {"more": True}, }, @@ -359,7 +359,7 @@ class PKOrderingQuestionAdmin(QuestionAdmin): { "results": [ {"id": str(q.pk), "text": q.question} - for q in Question.objects.all()[PAGINATOR_SIZE:] + for q in Question.objects.order_by("pk")[PAGINATOR_SIZE:] ], "pagination": {"more": False}, }, diff --git a/tests/composite_pk/tests.py b/tests/composite_pk/tests.py index 4284f9d627c0..3384ba157940 100644 --- a/tests/composite_pk/tests.py +++ b/tests/composite_pk/tests.py @@ -168,7 +168,7 @@ def test_in_bulk_batching(self): id_list = list(Comment.objects.values_list("pk", flat=True)) with self.assertNumQueries(2): comment_dict = Comment.objects.in_bulk(id_list=id_list) - self.assertQuerySetEqual(comment_dict, id_list) + self.assertCountEqual(comment_dict, id_list) def test_in_bulk_values(self): result = Comment.objects.values().in_bulk([self.comment.pk]) diff --git a/tests/defer/tests.py b/tests/defer/tests.py index 8aaa70fc19c2..89c6f4903857 100644 --- a/tests/defer/tests.py +++ b/tests/defer/tests.py @@ -104,7 +104,7 @@ def test_defer_values_does_not_defer(self): # User values() won't defer anything (you get the full list of # dictionaries back), but it still works. self.assertEqual( - Primary.objects.defer("name").values()[0], + Primary.objects.defer("name").values().get(pk=self.p1.pk), { "id": self.p1.id, "name": "p1", @@ -115,7 +115,7 @@ def test_defer_values_does_not_defer(self): def test_only_values_does_not_defer(self): self.assertEqual( - Primary.objects.only("name").values()[0], + Primary.objects.only("name").values().get(pk=self.p1.pk), { "id": self.p1.id, "name": "p1", @@ -138,7 +138,11 @@ def test_defer_with_select_related(self): self.assert_delayed(obj, 0) def test_only_with_select_related(self): - obj = Primary.objects.select_related("related").only("related__first")[0] + obj = ( + Primary.objects.select_related("related") + .only("related__first") + .get(pk=self.p1.pk) + ) self.assert_delayed(obj, 2) self.assert_delayed(obj.related, 1) self.assertEqual(obj.related_id, self.s1.pk) diff --git a/tests/expressions/test_queryset_values.py b/tests/expressions/test_queryset_values.py index 6264d0259450..175aacd1115a 100644 --- a/tests/expressions/test_queryset_values.py +++ b/tests/expressions/test_queryset_values.py @@ -33,7 +33,7 @@ def setUpTestData(cls): def test_values_expression(self): self.assertSequenceEqual( - Company.objects.values(salary=F("ceo__salary")), + Company.objects.values(salary=F("ceo__salary")).order_by("salary"), [{"salary": 10}, {"salary": 20}, {"salary": 30}], ) diff --git a/tests/expressions/tests.py b/tests/expressions/tests.py index 5effc8ac0d17..6fd7338d09ba 100644 --- a/tests/expressions/tests.py +++ b/tests/expressions/tests.py @@ -530,7 +530,7 @@ def test_ticket_16731_startswith_lookup(self): Employee.objects.create(firstname="John", lastname="Doe") e2 = Employee.objects.create(firstname="Jack", lastname="Jackson") e3 = Employee.objects.create(firstname="Jack", lastname="jackson") - self.assertSequenceEqual( + self.assertCountEqual( Employee.objects.filter(lastname__startswith=F("firstname")), [e2, e3] if connection.features.has_case_insensitive_like else [e2], ) @@ -813,7 +813,7 @@ def test_nested_subquery_join_outer_ref(self): ), ) self.assertSequenceEqual( - qs.values_list("ceo_company", flat=True), + qs.order_by("pk").values_list("ceo_company", flat=True), [self.example_inc.pk, self.foobar_ltd.pk, self.gmbh.pk], ) diff --git a/tests/foreign_object/tests.py b/tests/foreign_object/tests.py index 233c59688590..004174c0ea1e 100644 --- a/tests/foreign_object/tests.py +++ b/tests/foreign_object/tests.py @@ -494,8 +494,8 @@ def test_translations(self): # Test model initialization with active_translation field. a3 = Article(id=a3.id, pub_date=a3.pub_date, active_translation=at3_en) a3.save() - self.assertEqual( - list(Article.objects.filter(active_translation__abstract=None)), [a1, a3] + self.assertCountEqual( + Article.objects.filter(active_translation__abstract=None), [a1, a3] ) self.assertEqual( list( @@ -508,8 +508,8 @@ def test_translations(self): ) with translation.override("en"): - self.assertEqual( - list(Article.objects.filter(active_translation__abstract=None)), + self.assertCountEqual( + Article.objects.filter(active_translation__abstract=None), [a1, a2], ) diff --git a/tests/generic_relations/tests.py b/tests/generic_relations/tests.py index 8d07d1e63cb2..99832eca3e59 100644 --- a/tests/generic_relations/tests.py +++ b/tests/generic_relations/tests.py @@ -844,8 +844,7 @@ def test_fetch_mode_copied_reverse_fetching_one(self): self.assertEqual(tag._state.fetch_mode, FETCH_PEERS) def test_fetch_mode_copied_reverse_fetching_many(self): - animals = list(Animal.objects.fetch_mode(FETCH_PEERS)) - animal = animals[0] + animal = Animal.objects.fetch_mode(FETCH_PEERS).get(pk=self.lion.pk) self.assertEqual(animal._state.fetch_mode, FETCH_PEERS) tags = list(animal.tags.all()) tag = tags[0] diff --git a/tests/generic_relations_regress/tests.py b/tests/generic_relations_regress/tests.py index f10407b06020..5b2b59e5104d 100644 --- a/tests/generic_relations_regress/tests.py +++ b/tests/generic_relations_regress/tests.py @@ -194,10 +194,10 @@ def test_ticket_20378(self): self.assertSequenceEqual(HasLinkThing.objects.filter(links=l1), [hs3]) self.assertSequenceEqual(HasLinkThing.objects.filter(links=l2), [hs4]) self.assertSequenceEqual( - HasLinkThing.objects.exclude(links=l2), [hs1, hs2, hs3] + HasLinkThing.objects.exclude(links=l2).order_by("pk"), [hs1, hs2, hs3] ) self.assertSequenceEqual( - HasLinkThing.objects.exclude(links=l1), [hs1, hs2, hs4] + HasLinkThing.objects.exclude(links=l1).order_by("pk"), [hs1, hs2, hs4] ) def test_ticket_20564(self): diff --git a/tests/known_related_objects/tests.py b/tests/known_related_objects/tests.py index 371c73d3955b..fd6fc16ab1ac 100644 --- a/tests/known_related_objects/tests.py +++ b/tests/known_related_objects/tests.py @@ -176,11 +176,12 @@ def test_multilevel_reverse_fk_cyclic_select_related(self): def test_multilevel_reverse_fk_select_related(self): with self.assertNumQueries(2): - p = list( + p = ( Tournament.objects.filter(id=self.t2.id) .annotate( style=FilteredRelation("pool__another_style"), ) .select_related("style") + .filter(style__isnull=False) ) - self.assertEqual(p[0].style.another_pool, self.p3) + self.assertEqual(p.get().style.another_pool, self.p3) diff --git a/tests/m2m_recursive/tests.py b/tests/m2m_recursive/tests.py index e018434327c6..876ee4ae2a04 100644 --- a/tests/m2m_recursive/tests.py +++ b/tests/m2m_recursive/tests.py @@ -24,17 +24,19 @@ def test_recursive_m2m_all(self): (self.d, [self.a, self.c]), ): with self.subTest(person=person): - self.assertSequenceEqual(person.friends.all(), friends) + self.assertSequenceEqual(person.friends.order_by("name"), friends) def test_recursive_m2m_reverse_add(self): # Add m2m for Anne in reverse direction. self.b.friends.add(self.a) - self.assertSequenceEqual(self.a.friends.all(), [self.b, self.c, self.d]) + self.assertSequenceEqual( + self.a.friends.order_by("name"), [self.b, self.c, self.d] + ) self.assertSequenceEqual(self.b.friends.all(), [self.a]) def test_recursive_m2m_remove(self): self.b.friends.remove(self.a) - self.assertSequenceEqual(self.a.friends.all(), [self.c, self.d]) + self.assertSequenceEqual(self.a.friends.order_by("name"), [self.c, self.d]) self.assertSequenceEqual(self.b.friends.all(), []) def test_recursive_m2m_clear(self): @@ -94,7 +96,7 @@ def test_recursive_m2m_all(self): (self.d, [self.a, self.c]), ): with self.subTest(person=person): - self.assertSequenceEqual(person.colleagues.all(), colleagues) + self.assertSequenceEqual(person.colleagues.order_by("name"), colleagues) def test_recursive_m2m_reverse_add(self): # Add m2m for Anne in reverse direction. @@ -109,7 +111,7 @@ def test_recursive_m2m_reverse_add(self): def test_recursive_m2m_remove(self): self.b.colleagues.remove(self.a) - self.assertSequenceEqual(self.a.colleagues.all(), [self.c, self.d]) + self.assertSequenceEqual(self.a.colleagues.order_by("name"), [self.c, self.d]) self.assertSequenceEqual(self.b.colleagues.all(), []) def test_recursive_m2m_clear(self): diff --git a/tests/m2m_through/tests.py b/tests/m2m_through/tests.py index d89d89ed8fdb..3b4670e5b88c 100644 --- a/tests/m2m_through/tests.py +++ b/tests/m2m_through/tests.py @@ -483,7 +483,7 @@ def test_set_on_symmetrical_m2m_with_intermediate_model(self): [anne, kate], through_defaults={"date_friended": date_friended_set}, ) - self.assertSequenceEqual(tony.sym_friends.all(), [anne, kate]) + self.assertSequenceEqual(tony.sym_friends.order_by("name"), [anne, kate]) self.assertSequenceEqual(anne.sym_friends.all(), [tony]) self.assertSequenceEqual(kate.sym_friends.all(), [tony]) self.assertEqual( diff --git a/tests/many_to_one/tests.py b/tests/many_to_one/tests.py index e0def73db016..67ef46f3519f 100644 --- a/tests/many_to_one/tests.py +++ b/tests/many_to_one/tests.py @@ -828,7 +828,7 @@ def test_create_after_prefetch(self): city = City.objects.prefetch_related("districts").get(id=c.id) self.assertSequenceEqual(city.districts.all(), [d1]) d2 = city.districts.create(name="Goa") - self.assertSequenceEqual(city.districts.all(), [d1, d2]) + self.assertCountEqual(city.districts.all(), [d1, d2]) def test_clear_after_prefetch(self): c = City.objects.create(name="Musical City") diff --git a/tests/migrations/test_commands.py b/tests/migrations/test_commands.py index 61336f553328..81c9bf381618 100644 --- a/tests/migrations/test_commands.py +++ b/tests/migrations/test_commands.py @@ -1601,7 +1601,7 @@ def test_prune_respect_app_label(self): for migration in recorder.applied_migrations() if migration[0] in ["migrations", "migrations2"] ] - self.assertEqual( + self.assertCountEqual( applied_migrations, [ ("migrations", "0001_squashed_0002"), diff --git a/tests/model_fields/test_jsonfield.py b/tests/model_fields/test_jsonfield.py index 6171fa7c6d4c..21365109efa8 100644 --- a/tests/model_fields/test_jsonfield.py +++ b/tests/model_fields/test_jsonfield.py @@ -1374,7 +1374,7 @@ def test_filter(self): def test_filter_in(self): obj = NullableJSONModel.objects.create(value=JSONNull()) obj2 = NullableJSONModel.objects.create(value=[1]) - self.assertSequenceEqual( + self.assertCountEqual( NullableJSONModel.objects.filter(value__in=[JSONNull(), [1], "foo"]), [obj, obj2], ) @@ -1382,7 +1382,7 @@ def test_filter_in(self): def test_key_in(self): obj1 = NullableJSONModel.objects.create(value={"key": None}) obj2 = NullableJSONModel.objects.create(value={"key": [1]}) - self.assertSequenceEqual( + self.assertCountEqual( NullableJSONModel.objects.filter(value__key__in=[JSONNull(), [1], 0]), [obj1, obj2], ) @@ -1393,9 +1393,8 @@ def test_bulk_update(self): obj1.value = JSONNull() obj2.value = JSONNull() NullableJSONModel.objects.bulk_update([obj1, obj2], fields=["value"]) - self.assertSequenceEqual( - NullableJSONModel.objects.filter(value=JSONNull()), - [obj1, obj2], + self.assertCountEqual( + NullableJSONModel.objects.filter(value=JSONNull()), [obj1, obj2] ) def test_case_expression_with_jsonnull_then(self): diff --git a/tests/model_forms/models.py b/tests/model_forms/models.py index 83daa13d71f0..458bf8aef1d5 100644 --- a/tests/model_forms/models.py +++ b/tests/model_forms/models.py @@ -377,6 +377,9 @@ class FlexibleDatePost(models.Model): class Color(models.Model): name = models.CharField(max_length=50) + class Meta: + ordering = ("name",) + def __iter__(self): yield from range(5) @@ -388,6 +391,9 @@ class ColorfulItem(models.Model): name = models.CharField(max_length=50) colors = models.ManyToManyField(Color) + class Meta: + ordering = ("pk",) + class CustomErrorMessage(models.Model): name1 = models.CharField( diff --git a/tests/model_formsets/models.py b/tests/model_formsets/models.py index 397bf9de5387..413b351a531e 100644 --- a/tests/model_formsets/models.py +++ b/tests/model_formsets/models.py @@ -97,6 +97,9 @@ class Owner(models.Model): name = models.CharField(max_length=100) place = models.ForeignKey(Place, models.CASCADE) + class Meta: + ordering = ["auto_id"] + def __str__(self): return "%s at %s" % (self.name, self.place) diff --git a/tests/multiple_database/tests.py b/tests/multiple_database/tests.py index 42b2ba2486a1..60207ac9a4b8 100644 --- a/tests/multiple_database/tests.py +++ b/tests/multiple_database/tests.py @@ -931,8 +931,8 @@ def test_o2o_cross_database_protection(self): list(User.objects.using("default").values_list("username", flat=True)), ["alice"], ) - self.assertEqual( - list(User.objects.using("other").values_list("username", flat=True)), + self.assertCountEqual( + User.objects.using("other").values_list("username", flat=True), ["bob", "charlie"], ) self.assertEqual( diff --git a/tests/postgres_tests/test_array.py b/tests/postgres_tests/test_array.py index d6d2cacab519..b46166b53516 100644 --- a/tests/postgres_tests/test_array.py +++ b/tests/postgres_tests/test_array.py @@ -302,7 +302,7 @@ def test_isnull(self): ) def test_gt(self): - self.assertSequenceEqual( + self.assertCountEqual( NullableIntegerArrayModel.objects.filter(field__gt=[0]), self.objs[:4] ) @@ -312,7 +312,7 @@ def test_lt(self): ) def test_in(self): - self.assertSequenceEqual( + self.assertCountEqual( NullableIntegerArrayModel.objects.filter(field__in=[[1], [2]]), self.objs[:2], ) @@ -338,19 +338,19 @@ def test_in_including_F_object(self): ) def test_in_as_F_object(self): - self.assertSequenceEqual( + self.assertCountEqual( NullableIntegerArrayModel.objects.filter(field__in=[models.F("field")]), self.objs[:4], ) def test_contained_by(self): - self.assertSequenceEqual( + self.assertCountEqual( NullableIntegerArrayModel.objects.filter(field__contained_by=[1, 2]), self.objs[:2], ) def test_contained_by_including_F_object(self): - self.assertSequenceEqual( + self.assertCountEqual( NullableIntegerArrayModel.objects.filter( field__contained_by=[models.F("order"), 2] ), @@ -358,7 +358,7 @@ def test_contained_by_including_F_object(self): ) def test_contains(self): - self.assertSequenceEqual( + self.assertCountEqual( NullableIntegerArrayModel.objects.filter(field__contains=[2]), self.objs[1:3], ) @@ -371,7 +371,7 @@ def test_contains_subquery(self): self.objs[2:3], ) inner_qs = IntegerArrayModel.objects.filter(field__contains=OuterRef("field")) - self.assertSequenceEqual( + self.assertCountEqual( NullableIntegerArrayModel.objects.filter(Exists(inner_qs)), self.objs[1:3], ) @@ -411,7 +411,7 @@ def test_overlap_charfield_including_expression(self): obj_1 = CharArrayModel.objects.create(field=["TEXT", "lower text"]) obj_2 = CharArrayModel.objects.create(field=["lower text", "TEXT"]) CharArrayModel.objects.create(field=["lower text", "text"]) - self.assertSequenceEqual( + self.assertCountEqual( CharArrayModel.objects.filter( field__overlap=[ Upper(Value("text")), @@ -493,12 +493,12 @@ def test_order_by_arrayagg_index(self): ) def test_index(self): - self.assertSequenceEqual( + self.assertCountEqual( NullableIntegerArrayModel.objects.filter(field__0=2), self.objs[1:3] ) def test_index_chained(self): - self.assertSequenceEqual( + self.assertCountEqual( NullableIntegerArrayModel.objects.filter(field__0__lt=3), self.objs[0:3] ) @@ -535,13 +535,13 @@ def test_index_annotation(self): ) def test_overlap(self): - self.assertSequenceEqual( + self.assertCountEqual( NullableIntegerArrayModel.objects.filter(field__overlap=[1, 2]), self.objs[0:3], ) def test_len(self): - self.assertSequenceEqual( + self.assertCountEqual( NullableIntegerArrayModel.objects.filter(field__len__lte=2), self.objs[0:3] ) @@ -552,7 +552,7 @@ def test_len_empty_array(self): ) def test_slice(self): - self.assertSequenceEqual( + self.assertCountEqual( NullableIntegerArrayModel.objects.filter(field__0_1=[2]), self.objs[1:3] ) @@ -639,7 +639,7 @@ def test_slicing_of_f_expressions_with_len(self): queryset = NullableIntegerArrayModel.objects.annotate( subarray=F("field")[:1] ).filter(field__len=F("subarray__len")) - self.assertSequenceEqual(queryset, self.objs[:2]) + self.assertCountEqual(queryset, self.objs[:2]) def test_usage_in_subquery(self): self.assertSequenceEqual( @@ -697,7 +697,7 @@ def test_filter_by_array_subquery(self): inner_qs = NullableIntegerArrayModel.objects.filter( field__len=models.OuterRef("field__len"), ).values("field") - self.assertSequenceEqual( + self.assertCountEqual( NullableIntegerArrayModel.objects.alias( same_sized_fields=ArraySubquery(inner_qs), ).filter(same_sized_fields__len__gt=1), diff --git a/tests/postgres_tests/test_bulk_update.py b/tests/postgres_tests/test_bulk_update.py index f3e8ac8a4057..880ca32dccce 100644 --- a/tests/postgres_tests/test_bulk_update.py +++ b/tests/postgres_tests/test_bulk_update.py @@ -42,9 +42,7 @@ def test_bulk_update(self): for instance in instances: setattr(instance, field, new) Model.objects.bulk_update(instances, [field]) - self.assertSequenceEqual( - Model.objects.filter(**{field: new}), instances - ) + self.assertCountEqual(Model.objects.filter(**{field: new}), instances) def test_bulk_create(self): OffByOneModel.objects.bulk_create(OffByOneModel(one_off=0) for _ in range(20)) diff --git a/tests/postgres_tests/test_hstore.py b/tests/postgres_tests/test_hstore.py index 2d19364736be..9fdb19aa36f8 100644 --- a/tests/postgres_tests/test_hstore.py +++ b/tests/postgres_tests/test_hstore.py @@ -91,13 +91,13 @@ def test_exact(self): ) def test_contained_by(self): - self.assertSequenceEqual( + self.assertCountEqual( HStoreModel.objects.filter(field__contained_by={"a": "b", "c": "d"}), self.objs[:4], ) def test_contains(self): - self.assertSequenceEqual( + self.assertCountEqual( HStoreModel.objects.filter(field__contains={"a": "b"}), self.objs[:2] ) @@ -110,7 +110,7 @@ def search(): ) def test_has_key(self): - self.assertSequenceEqual( + self.assertCountEqual( HStoreModel.objects.filter(field__has_key="c"), self.objs[1:3] ) @@ -120,18 +120,16 @@ def test_has_keys(self): ) def test_has_any_keys(self): - self.assertSequenceEqual( + self.assertCountEqual( HStoreModel.objects.filter(field__has_any_keys=["a", "c"]), self.objs[:3] ) def test_key_transform(self): - self.assertSequenceEqual( - HStoreModel.objects.filter(field__a="b"), self.objs[:2] - ) + self.assertCountEqual(HStoreModel.objects.filter(field__a="b"), self.objs[:2]) def test_key_transform_raw_expression(self): expr = RawSQL("%s::hstore", ["x => b, y => c"]) - self.assertSequenceEqual( + self.assertCountEqual( HStoreModel.objects.filter(field__a=KeyTransform("x", expr)), self.objs[:2] ) @@ -153,7 +151,7 @@ def test_values(self): ) def test_field_chaining_contains(self): - self.assertSequenceEqual( + self.assertCountEqual( HStoreModel.objects.filter(field__a__contains="b"), self.objs[:2] ) @@ -170,7 +168,7 @@ def test_field_chaining_startswith(self): ) def test_field_chaining_istartswith(self): - self.assertSequenceEqual( + self.assertCountEqual( HStoreModel.objects.filter(field__cat__istartswith="kit"), self.objs[7:], ) @@ -182,13 +180,13 @@ def test_field_chaining_endswith(self): ) def test_field_chaining_iendswith(self): - self.assertSequenceEqual( + self.assertCountEqual( HStoreModel.objects.filter(field__cat__iendswith="ou"), self.objs[5:7], ) def test_field_chaining_iexact(self): - self.assertSequenceEqual( + self.assertCountEqual( HStoreModel.objects.filter(field__breed__iexact="persian"), self.objs[7:], ) @@ -200,7 +198,7 @@ def test_field_chaining_regex(self): ) def test_field_chaining_iregex(self): - self.assertSequenceEqual( + self.assertCountEqual( HStoreModel.objects.filter(field__cat__iregex=r"oU$"), self.objs[5:7], ) @@ -218,27 +216,27 @@ def test_order_by_field(self): ) def test_keys_contains(self): - self.assertSequenceEqual( + self.assertCountEqual( HStoreModel.objects.filter(field__keys__contains=["a"]), self.objs[:2] ) def test_values_overlap(self): - self.assertSequenceEqual( + self.assertCountEqual( HStoreModel.objects.filter(field__values__overlap=["b", "d"]), self.objs[:3] ) def test_key_isnull(self): obj = HStoreModel.objects.create(field={"a": None}) - self.assertSequenceEqual( + self.assertCountEqual( HStoreModel.objects.filter(field__a__isnull=True), self.objs[2:9] + [obj], ) - self.assertSequenceEqual( + self.assertCountEqual( HStoreModel.objects.filter(field__a__isnull=False), self.objs[:2] ) def test_usage_in_subquery(self): - self.assertSequenceEqual( + self.assertCountEqual( HStoreModel.objects.filter(id__in=HStoreModel.objects.filter(field__a="b")), self.objs[:2], ) @@ -263,7 +261,7 @@ def test_obj_subquery_lookup(self): HStoreModel.objects.filter(pk=OuterRef("pk")).values("field") ), ).filter(value__a="b") - self.assertSequenceEqual(qs, self.objs[:2]) + self.assertCountEqual(qs, self.objs[:2]) @isolate_apps("postgres_tests") diff --git a/tests/postgres_tests/test_ranges.py b/tests/postgres_tests/test_ranges.py index 7e21d2704ff9..c8f08ee99f29 100644 --- a/tests/postgres_tests/test_ranges.py +++ b/tests/postgres_tests/test_ranges.py @@ -293,7 +293,7 @@ def test_isempty(self): ) def test_contains(self): - self.assertSequenceEqual( + self.assertCountEqual( RangesModel.objects.filter(ints__contains=8), [self.objs[0], self.objs[1]], ) @@ -319,18 +319,18 @@ def test_decimal_contains_range(self): (15, [decimals[1], decimals[3]]), ]: with self.subTest(decimal_contains=contains): - self.assertSequenceEqual( + self.assertCountEqual( RangesModel.objects.filter(decimals__contains=contains), objs ) def test_contained_by(self): - self.assertSequenceEqual( + self.assertCountEqual( RangesModel.objects.filter(ints__contained_by=NumericRange(0, 20)), [self.objs[0], self.objs[1], self.objs[3]], ) def test_overlap(self): - self.assertSequenceEqual( + self.assertCountEqual( RangesModel.objects.filter(ints__overlap=NumericRange(3, 8)), [self.objs[0], self.objs[1]], ) @@ -354,13 +354,13 @@ def test_not_lt(self): ) def test_not_gt(self): - self.assertSequenceEqual( + self.assertCountEqual( RangesModel.objects.filter(ints__not_gt=NumericRange(5, 10)), [self.objs[0], self.objs[2]], ) def test_adjacent_to(self): - self.assertSequenceEqual( + self.assertCountEqual( RangesModel.objects.filter(ints__adjacent_to=NumericRange(0, 5)), [self.objs[1], self.objs[2]], ) @@ -378,7 +378,7 @@ def test_endswith(self): ) def test_startswith_chaining(self): - self.assertSequenceEqual( + self.assertCountEqual( RangesModel.objects.filter(ints__startswith__gte=0), [self.objs[0], self.objs[1]], ) @@ -404,7 +404,7 @@ def test_bound_type(self): ] for lookup, filter_arg, excepted_result in tests: with self.subTest(lookup=lookup, filter_arg=filter_arg): - self.assertSequenceEqual( + self.assertCountEqual( RangesModel.objects.filter(**{"decimals__%s" % lookup: filter_arg}), excepted_result, ) @@ -516,7 +516,7 @@ def test_small_auto_field_contained_by(self): objs = SmallAutoFieldModel.objects.bulk_create( [SmallAutoFieldModel() for i in range(1, 5)] ) - self.assertSequenceEqual( + self.assertCountEqual( SmallAutoFieldModel.objects.filter( id__contained_by=NumericRange(objs[1].pk, objs[3].pk), ), @@ -527,7 +527,7 @@ def test_auto_field_contained_by(self): objs = RangeLookupsModel.objects.bulk_create( [RangeLookupsModel() for i in range(1, 5)] ) - self.assertSequenceEqual( + self.assertCountEqual( RangeLookupsModel.objects.filter( id__contained_by=NumericRange(objs[1].pk, objs[3].pk), ), @@ -538,7 +538,7 @@ def test_big_auto_field_contained_by(self): objs = BigAutoFieldModel.objects.bulk_create( [BigAutoFieldModel() for i in range(1, 5)] ) - self.assertSequenceEqual( + self.assertCountEqual( BigAutoFieldModel.objects.filter( id__contained_by=NumericRange(objs[1].pk, objs[3].pk), ), diff --git a/tests/postgres_tests/test_search.py b/tests/postgres_tests/test_search.py index c206c69747d2..fb4678900930 100644 --- a/tests/postgres_tests/test_search.py +++ b/tests/postgres_tests/test_search.py @@ -295,7 +295,7 @@ def test_web_search(self): search_type="websearch", ), ) - self.assertSequenceEqual(searched, [self.verse0, self.verse1]) + self.assertCountEqual(searched, [self.verse0, self.verse1]) def test_web_search_with_config(self): line_qs = Line.objects.annotate( @@ -928,7 +928,7 @@ def test_lexemes_multiple_or(self): ) ) - self.assertSequenceEqual(searched, [self.verse0, self.verse1]) + self.assertCountEqual(searched, [self.verse0, self.verse1]) def test_config_query_explicit(self): searched = Line.objects.annotate( diff --git a/tests/proxy_models/tests.py b/tests/proxy_models/tests.py index e7d4ec714fe2..4e769582ebc9 100644 --- a/tests/proxy_models/tests.py +++ b/tests/proxy_models/tests.py @@ -297,7 +297,7 @@ def test_proxy_delete(self): User.objects.create(name="Bruce") u2 = UserProxy.objects.create(name="George") - resp = [u.name for u in UserProxy.objects.all()] + resp = [u.name for u in UserProxy.objects.order_by("name")] self.assertEqual(resp, ["Bruce", "George"]) u2.delete() diff --git a/tests/queries/test_qs_combinators.py b/tests/queries/test_qs_combinators.py index 944b360ee63e..f30f3aae1430 100644 --- a/tests/queries/test_qs_combinators.py +++ b/tests/queries/test_qs_combinators.py @@ -92,9 +92,9 @@ def test_union_none_slice(self): def test_union_empty_slice(self): qs = Number.objects.union() - self.assertNumbersEqual(qs[:1], [0]) + self.assertNumbersEqual(qs.order_by("num")[:1], [0]) qs = Number.objects.union(all=True) - self.assertNumbersEqual(qs[:1], [0]) + self.assertNumbersEqual(qs.order_by("num")[:1], [0]) self.assertNumbersEqual(qs.order_by("num")[0:], list(range(0, 10))) def test_union_all_none_slice(self): diff --git a/tests/queries/tests.py b/tests/queries/tests.py index 71d0b30196fe..c987649fac25 100644 --- a/tests/queries/tests.py +++ b/tests/queries/tests.py @@ -2539,19 +2539,19 @@ def setUpTestData(cls): @skipUnlessDBFeature("allow_sliced_subqueries_with_in") def test_or_with_rhs_slice(self): qs1 = Classroom.objects.filter(has_blackboard=True) - qs2 = Classroom.objects.filter(has_blackboard=False)[:1] + qs2 = Classroom.objects.filter(has_blackboard=False).order_by("pk")[:1] self.assertCountEqual(qs1 | qs2, [self.room_1, self.room_2, self.room_3]) @skipUnlessDBFeature("allow_sliced_subqueries_with_in") def test_or_with_lhs_slice(self): - qs1 = Classroom.objects.filter(has_blackboard=True)[:1] + qs1 = Classroom.objects.filter(has_blackboard=True).order_by("pk")[:1] qs2 = Classroom.objects.filter(has_blackboard=False) self.assertCountEqual(qs1 | qs2, [self.room_1, self.room_2, self.room_4]) @skipUnlessDBFeature("allow_sliced_subqueries_with_in") def test_or_with_both_slice(self): - qs1 = Classroom.objects.filter(has_blackboard=False)[:1] - qs2 = Classroom.objects.filter(has_blackboard=True)[:1] + qs1 = Classroom.objects.filter(has_blackboard=False).order_by("pk")[:1] + qs2 = Classroom.objects.filter(has_blackboard=True).order_by("pk")[:1] self.assertCountEqual(qs1 | qs2, [self.room_1, self.room_2]) @skipUnlessDBFeature("allow_sliced_subqueries_with_in") @@ -2563,19 +2563,19 @@ def test_or_with_both_slice_and_ordering(self): @skipUnlessDBFeature("allow_sliced_subqueries_with_in") def test_xor_with_rhs_slice(self): qs1 = Classroom.objects.filter(has_blackboard=True) - qs2 = Classroom.objects.filter(has_blackboard=False)[:1] + qs2 = Classroom.objects.filter(has_blackboard=False).order_by("pk")[:1] self.assertCountEqual(qs1 ^ qs2, [self.room_1, self.room_2, self.room_3]) @skipUnlessDBFeature("allow_sliced_subqueries_with_in") def test_xor_with_lhs_slice(self): - qs1 = Classroom.objects.filter(has_blackboard=True)[:1] + qs1 = Classroom.objects.filter(has_blackboard=True).order_by("pk")[:1] qs2 = Classroom.objects.filter(has_blackboard=False) self.assertCountEqual(qs1 ^ qs2, [self.room_1, self.room_2, self.room_4]) @skipUnlessDBFeature("allow_sliced_subqueries_with_in") def test_xor_with_both_slice(self): - qs1 = Classroom.objects.filter(has_blackboard=False)[:1] - qs2 = Classroom.objects.filter(has_blackboard=True)[:1] + qs1 = Classroom.objects.filter(has_blackboard=False).order_by("pk")[:1] + qs2 = Classroom.objects.filter(has_blackboard=True).order_by("pk")[:1] self.assertCountEqual(qs1 ^ qs2, [self.room_1, self.room_2]) @skipUnlessDBFeature("allow_sliced_subqueries_with_in") diff --git a/tests/raw_query/tests.py b/tests/raw_query/tests.py index f5485e733f6b..83e06c4cb39c 100644 --- a/tests/raw_query/tests.py +++ b/tests/raw_query/tests.py @@ -137,8 +137,8 @@ def test_simple_raw_query(self): """ Basic test of raw query with a simple database query """ - query = "SELECT * FROM raw_query_author" - authors = Author.objects.all() + query = "SELECT * FROM raw_query_author ORDER BY id" + authors = Author.objects.order_by("pk") self.assertSuccessfulRawQuery(Author, query, authors) def test_raw_query_lazy(self): @@ -155,8 +155,8 @@ def test_FK_raw_query(self): """ Test of a simple raw query against a model containing a foreign key """ - query = "SELECT * FROM raw_query_book" - books = Book.objects.all() + query = "SELECT * FROM raw_query_book ORDER BY id" + books = Book.objects.order_by("pk") self.assertSuccessfulRawQuery(Book, query, books) def test_fk_fetch_mode_peers(self): @@ -180,8 +180,8 @@ def test_db_column_handler(self): Test of a simple raw query against a model containing a field with db_column defined. """ - query = "SELECT * FROM raw_query_coffee" - coffees = Coffee.objects.all() + query = "SELECT * FROM raw_query_coffee ORDER BY id" + coffees = Coffee.objects.order_by("pk") self.assertSuccessfulRawQuery(Coffee, query, coffees) def test_pk_with_mixed_case_db_column(self): @@ -201,8 +201,8 @@ def test_order_handler(self): ) for select in selects: - query = "SELECT %s FROM raw_query_author" % select - authors = Author.objects.all() + query = "SELECT %s FROM raw_query_author ORDER BY id" % select + authors = Author.objects.order_by("pk") self.assertSuccessfulRawQuery(Author, query, authors) def test_translations(self): @@ -212,10 +212,10 @@ def test_translations(self): """ query = ( "SELECT first_name AS first, last_name AS last, dob, id " - "FROM raw_query_author" + "FROM raw_query_author ORDER BY id" ) translations = {"first": "first_name", "last": "last_name"} - authors = Author.objects.all() + authors = Author.objects.order_by("pk") self.assertSuccessfulRawQuery(Author, query, authors, translations=translations) def test_params(self): @@ -287,15 +287,15 @@ def test_many_to_many(self): """ Test of a simple raw query against a model containing a m2m field """ - query = "SELECT * FROM raw_query_reviewer" - reviewers = Reviewer.objects.all() + query = "SELECT * FROM raw_query_reviewer ORDER BY id" + reviewers = Reviewer.objects.order_by("pk") self.assertSuccessfulRawQuery(Reviewer, query, reviewers) def test_extra_conversions(self): """Extra translations are ignored.""" - query = "SELECT * FROM raw_query_author" + query = "SELECT * FROM raw_query_author ORDER BY id" translations = {"something": "else"} - authors = Author.objects.all() + authors = Author.objects.order_by("pk") self.assertSuccessfulRawQuery(Author, query, authors, translations=translations) def test_missing_fields(self): @@ -345,13 +345,13 @@ def test_annotations(self): self.assertSuccessfulRawQuery(Author, query, authors, expected_annotations) def test_white_space_query(self): - query = " SELECT * FROM raw_query_author" - authors = Author.objects.all() + query = " SELECT * FROM raw_query_author ORDER BY id" + authors = Author.objects.order_by("pk") self.assertSuccessfulRawQuery(Author, query, authors) def test_multiple_iterations(self): - query = "SELECT * FROM raw_query_author" - normal_authors = Author.objects.all() + query = "SELECT * FROM raw_query_author ORDER BY id" + normal_authors = Author.objects.order_by("pk") raw_authors = Author.objects.raw(query) # First Iteration From fdfbb711e3dd52683ae633f568761efe2735d5f0 Mon Sep 17 00:00:00 2001 From: Aslan Osmanov Date: Sun, 9 Aug 2026 13:04:20 +0500 Subject: [PATCH 2/2] Fixed #37296 -- Included the invalid view type in path()/re_path() error. The fallback TypeError raised by path() and re_path() for an invalid view did not report the received type, unlike the neighboring errors for an invalid kwargs argument or a class-based view instance. --- AUTHORS | 1 + django/urls/conf.py | 3 ++- tests/urlpatterns/tests.py | 5 ++++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index e2aca4ea3b98..22eb211b9abe 100644 --- a/AUTHORS +++ b/AUTHORS @@ -135,6 +135,7 @@ answer newbie questions, and generally made Django that much better: Aryeh Leib Taurog A S Alam Asif Saif Uddin + Aslan Osmanov atlithorn Audrey Roy av0000@mail.ru diff --git a/django/urls/conf.py b/django/urls/conf.py index bb61063d7708..20afbc26c13d 100644 --- a/django/urls/conf.py +++ b/django/urls/conf.py @@ -88,7 +88,8 @@ def _path(route, view, kwargs=None, name=None, Pattern=None): ) else: raise TypeError( - "view must be a callable or a list/tuple in the case of include()." + "view must be a callable or a list/tuple in the case of " + f"include(), but got {view.__class__.__name__}." ) diff --git a/tests/urlpatterns/tests.py b/tests/urlpatterns/tests.py index 241e1e65d8cc..15f49cdee0e1 100644 --- a/tests/urlpatterns/tests.py +++ b/tests/urlpatterns/tests.py @@ -218,7 +218,10 @@ def test_warning_override_converter(self): REGISTERED_CONVERTERS.pop("base64", None) def test_invalid_view(self): - msg = "view must be a callable or a list/tuple in the case of include()." + msg = ( + "view must be a callable or a list/tuple in the case of include(), " + "but got str." + ) with self.assertRaisesMessage(TypeError, msg): path("articles/", "invalid_view")