From 76061219f899738b26a6473497de36879ebe0441 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Sat, 23 May 2026 18:32:36 -0400 Subject: [PATCH 1/3] Removed hardcoded pks in many tests. --- tests/admin_changelist/tests.py | 12 +- tests/admin_inlines/tests.py | 2 +- tests/admin_views/tests.py | 127 +++++++++++----------- tests/delete_regress/tests.py | 2 +- tests/force_insert_update/tests.py | 4 +- tests/forms_tests/tests/tests.py | 86 ++++++++------- tests/model_formsets/tests.py | 114 +++++++++---------- tests/multiple_database/tests.py | 10 +- tests/order_with_respect_to/base_tests.py | 12 +- tests/queries/tests.py | 30 ++--- tests/sites_framework/tests.py | 19 ++-- 11 files changed, 209 insertions(+), 209 deletions(-) diff --git a/tests/admin_changelist/tests.py b/tests/admin_changelist/tests.py index ba1f5d179365..f1d1abc0b0a5 100644 --- a/tests/admin_changelist/tests.py +++ b/tests/admin_changelist/tests.py @@ -1148,16 +1148,20 @@ def test_dynamic_list_display_links(self): Regression tests for #16257: dynamic list_display_links support. """ parent = Parent.objects.create(name="parent") + children = [] for i in range(1, 10): - Child.objects.create(id=i, name="child %s" % i, parent=parent, age=i) + children.append( + Child.objects.create(name="child %s" % i, parent=parent, age=i + 1) + ) m = DynamicListDisplayLinksChildAdmin(Child, custom_site) superuser = self._create_superuser("superuser") request = self._mocked_authenticated_request("/child/", superuser) response = m.changelist_view(request) - for i in range(1, 10): - link = reverse("admin:admin_changelist_child_change", args=(i,)) - self.assertContains(response, '%s' % (link, i)) + for child in children: + link = reverse("admin:admin_changelist_child_change", args=(child.pk,)) + # "age" is in list_display_links. + self.assertContains(response, '%s' % (link, child.age)) list_display = m.get_list_display(request) list_display_links = m.get_list_display_links(request, list_display) diff --git a/tests/admin_inlines/tests.py b/tests/admin_inlines/tests.py index eda7c91310e9..62e97e317a54 100644 --- a/tests/admin_inlines/tests.py +++ b/tests/admin_inlines/tests.py @@ -960,7 +960,7 @@ def setUpTestData(cls): ) cls.user.user_permissions.add(permission) - author = Author.objects.create(pk=1, name="The Author") + author = Author.objects.create(name="The Author") cls.book = author.books.create(name="The inline Book") cls.author_change_url = reverse( "admin:admin_inlines_author_change", args=(author.id,) diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index b6c4e88a43ef..eb43194bcc1b 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -1893,7 +1893,7 @@ def test_custom_model_admin_templates(self): data={ "index": 0, "action": ["delete_selected"], - "_selected_action": ["1"], + "_selected_action": [str(article_pk)], }, ) self.assertTemplateUsed( @@ -4901,30 +4901,28 @@ def test_non_form_errors_is_errorlist(self): ) def test_list_editable_ordering(self): - collector = Collector.objects.create(id=1, name="Frederick Clegg") - - Category.objects.create(id=1, order=1, collector=collector) - Category.objects.create(id=2, order=2, collector=collector) - Category.objects.create(id=3, order=0, collector=collector) - Category.objects.create(id=4, order=0, collector=collector) - + collector = Collector.objects.create(name="Frederick Clegg") + category1 = Category.objects.create(order=1, collector=collector) + category2 = Category.objects.create(order=2, collector=collector) + category3 = Category.objects.create(order=0, collector=collector) + category4 = Category.objects.create(order=0, collector=collector) # NB: The order values must be changed so that the items are reordered. data = { "form-TOTAL_FORMS": "4", "form-INITIAL_FORMS": "4", "form-MAX_NUM_FORMS": "0", "form-0-order": "14", - "form-0-id": "1", - "form-0-collector": "1", + "form-0-id": str(category1.id), + "form-0-collector": str(collector.id), "form-1-order": "13", - "form-1-id": "2", - "form-1-collector": "1", + "form-1-id": str(category2.id), + "form-1-collector": str(collector.id), "form-2-order": "1", - "form-2-id": "3", - "form-2-collector": "1", + "form-2-id": str(category3.id), + "form-2-collector": str(collector.id), "form-3-order": "0", - "form-3-id": "4", - "form-3-collector": "1", + "form-3-id": str(category4.id), + "form-3-collector": str(collector.id), # The form processing understands this as a list_editable "Save" # and not an action "Run". "_save": "Save", @@ -4936,18 +4934,18 @@ def test_list_editable_ordering(self): self.assertEqual(response.status_code, 302) # The order values have been applied to the right objects - self.assertEqual(Category.objects.get(id=1).order, 14) - self.assertEqual(Category.objects.get(id=2).order, 13) - self.assertEqual(Category.objects.get(id=3).order, 1) - self.assertEqual(Category.objects.get(id=4).order, 0) + self.assertEqual(Category.objects.get(id=category1.id).order, 14) + self.assertEqual(Category.objects.get(id=category2.id).order, 13) + self.assertEqual(Category.objects.get(id=category3.id).order, 1) + self.assertEqual(Category.objects.get(id=category4.id).order, 0) def test_list_editable_pagination(self): """ Pagination works for list_editable items. """ - UnorderedObject.objects.create(id=1, name="Unordered object #1") - UnorderedObject.objects.create(id=2, name="Unordered object #2") - UnorderedObject.objects.create(id=3, name="Unordered object #3") + UnorderedObject.objects.create(name="Unordered object #1") + UnorderedObject.objects.create(name="Unordered object #2") + UnorderedObject.objects.create(name="Unordered object #3") response = self.client.get( reverse("admin:admin_views_unorderedobject_changelist") ) @@ -4969,12 +4967,12 @@ def test_list_editable_action_submit(self): "form-INITIAL_FORMS": "3", "form-MAX_NUM_FORMS": "0", "form-0-gender": "1", - "form-0-id": "1", + "form-0-id": str(self.per1.id), "form-1-gender": "2", - "form-1-id": "2", + "form-1-id": str(self.per2.id), "form-2-alive": "checked", "form-2-gender": "1", - "form-2-id": "3", + "form-2-id": str(self.per3.id), "index": "0", "_selected_action": ["3"], "action": ["", "delete_selected"], @@ -5830,7 +5828,7 @@ def setUpTestData(cls): cls.superuser = User.objects.create_superuser( username="super", password="secret", email="super@example.com" ) - cls.collector = Collector.objects.create(pk=1, name="John Fowles") + cls.collector = Collector.objects.create(name="John Fowles") def setUp(self): self.post_data = { @@ -5839,63 +5837,63 @@ def setUp(self): "widget_set-INITIAL_FORMS": "0", "widget_set-MAX_NUM_FORMS": "0", "widget_set-0-id": "", - "widget_set-0-owner": "1", + "widget_set-0-owner": str(self.collector.pk), "widget_set-0-name": "", "widget_set-1-id": "", - "widget_set-1-owner": "1", + "widget_set-1-owner": str(self.collector.pk), "widget_set-1-name": "", "widget_set-2-id": "", - "widget_set-2-owner": "1", + "widget_set-2-owner": str(self.collector.pk), "widget_set-2-name": "", "doohickey_set-TOTAL_FORMS": "3", "doohickey_set-INITIAL_FORMS": "0", "doohickey_set-MAX_NUM_FORMS": "0", - "doohickey_set-0-owner": "1", + "doohickey_set-0-owner": str(self.collector.pk), "doohickey_set-0-code": "", "doohickey_set-0-name": "", - "doohickey_set-1-owner": "1", + "doohickey_set-1-owner": str(self.collector.pk), "doohickey_set-1-code": "", "doohickey_set-1-name": "", - "doohickey_set-2-owner": "1", + "doohickey_set-2-owner": str(self.collector.pk), "doohickey_set-2-code": "", "doohickey_set-2-name": "", "grommet_set-TOTAL_FORMS": "3", "grommet_set-INITIAL_FORMS": "0", "grommet_set-MAX_NUM_FORMS": "0", "grommet_set-0-code": "", - "grommet_set-0-owner": "1", + "grommet_set-0-owner": str(self.collector.pk), "grommet_set-0-name": "", "grommet_set-1-code": "", - "grommet_set-1-owner": "1", + "grommet_set-1-owner": str(self.collector.pk), "grommet_set-1-name": "", "grommet_set-2-code": "", - "grommet_set-2-owner": "1", + "grommet_set-2-owner": str(self.collector.pk), "grommet_set-2-name": "", "whatsit_set-TOTAL_FORMS": "3", "whatsit_set-INITIAL_FORMS": "0", "whatsit_set-MAX_NUM_FORMS": "0", - "whatsit_set-0-owner": "1", + "whatsit_set-0-owner": str(self.collector.pk), "whatsit_set-0-index": "", "whatsit_set-0-name": "", - "whatsit_set-1-owner": "1", + "whatsit_set-1-owner": str(self.collector.pk), "whatsit_set-1-index": "", "whatsit_set-1-name": "", - "whatsit_set-2-owner": "1", + "whatsit_set-2-owner": str(self.collector.pk), "whatsit_set-2-index": "", "whatsit_set-2-name": "", "fancydoodad_set-TOTAL_FORMS": "3", "fancydoodad_set-INITIAL_FORMS": "0", "fancydoodad_set-MAX_NUM_FORMS": "0", "fancydoodad_set-0-doodad_ptr": "", - "fancydoodad_set-0-owner": "1", + "fancydoodad_set-0-owner": str(self.collector.pk), "fancydoodad_set-0-name": "", "fancydoodad_set-0-expensive": "on", "fancydoodad_set-1-doodad_ptr": "", - "fancydoodad_set-1-owner": "1", + "fancydoodad_set-1-owner": str(self.collector.pk), "fancydoodad_set-1-name": "", "fancydoodad_set-1-expensive": "on", "fancydoodad_set-2-doodad_ptr": "", - "fancydoodad_set-2-owner": "1", + "fancydoodad_set-2-owner": str(self.collector.pk), "fancydoodad_set-2-name": "", "fancydoodad_set-2-expensive": "on", "category_set-TOTAL_FORMS": "3", @@ -5903,13 +5901,13 @@ def setUp(self): "category_set-MAX_NUM_FORMS": "0", "category_set-0-order": "", "category_set-0-id": "", - "category_set-0-collector": "1", + "category_set-0-collector": str(self.collector.pk), "category_set-1-order": "", "category_set-1-id": "", - "category_set-1-collector": "1", + "category_set-1-collector": str(self.collector.pk), "category_set-2-order": "", "category_set-2-id": "", - "category_set-2-collector": "1", + "category_set-2-collector": str(self.collector.pk), } self.client.force_login(self.superuser) @@ -6105,11 +6103,10 @@ def test_ordered_inline(self): An inline with an editable ordering fields is updated correctly. """ # Create some objects with an initial ordering - Category.objects.create(id=1, order=1, collector=self.collector) - Category.objects.create(id=2, order=2, collector=self.collector) - Category.objects.create(id=3, order=0, collector=self.collector) - Category.objects.create(id=4, order=0, collector=self.collector) - + category1 = Category.objects.create(order=1, collector=self.collector) + category2 = Category.objects.create(order=2, collector=self.collector) + category3 = Category.objects.create(order=0, collector=self.collector) + category4 = Category.objects.create(order=0, collector=self.collector) # NB: The order values must be changed so that the items are reordered. self.post_data.update( { @@ -6118,26 +6115,26 @@ def test_ordered_inline(self): "category_set-INITIAL_FORMS": "4", "category_set-MAX_NUM_FORMS": "0", "category_set-0-order": "14", - "category_set-0-id": "1", - "category_set-0-collector": "1", + "category_set-0-id": str(category1.id), + "category_set-0-collector": str(self.collector.id), "category_set-1-order": "13", - "category_set-1-id": "2", - "category_set-1-collector": "1", + "category_set-1-id": str(category2.id), + "category_set-1-collector": str(self.collector.pk), "category_set-2-order": "1", - "category_set-2-id": "3", - "category_set-2-collector": "1", + "category_set-2-id": str(category3.id), + "category_set-2-collector": str(self.collector.pk), "category_set-3-order": "0", - "category_set-3-id": "4", - "category_set-3-collector": "1", + "category_set-3-id": str(category4.id), + "category_set-3-collector": str(self.collector.pk), "category_set-4-order": "", "category_set-4-id": "", - "category_set-4-collector": "1", + "category_set-4-collector": str(self.collector.pk), "category_set-5-order": "", "category_set-5-id": "", - "category_set-5-collector": "1", + "category_set-5-collector": str(self.collector.pk), "category_set-6-order": "", "category_set-6-id": "", - "category_set-6-collector": "1", + "category_set-6-collector": str(self.collector.pk), } ) collector_url = reverse( @@ -6149,10 +6146,10 @@ def test_ordered_inline(self): # The order values have been applied to the right objects self.assertEqual(self.collector.category_set.count(), 4) - self.assertEqual(Category.objects.get(id=1).order, 14) - self.assertEqual(Category.objects.get(id=2).order, 13) - self.assertEqual(Category.objects.get(id=3).order, 1) - self.assertEqual(Category.objects.get(id=4).order, 0) + self.assertEqual(Category.objects.get(id=category1.id).order, 14) + self.assertEqual(Category.objects.get(id=category2.id).order, 13) + self.assertEqual(Category.objects.get(id=category3.id).order, 1) + self.assertEqual(Category.objects.get(id=category4.id).order, 0) @override_settings(ROOT_URLCONF="admin_views.urls") diff --git a/tests/delete_regress/tests.py b/tests/delete_regress/tests.py index dc1039f7b98b..40f556bb85c0 100644 --- a/tests/delete_regress/tests.py +++ b/tests/delete_regress/tests.py @@ -117,7 +117,7 @@ def test_fk_to_m2m_through(self): self.assertEqual(PlayedWithNote.objects.count(), 0) def test_15776(self): - policy = Policy.objects.create(pk=1, policy_number="1234") + policy = Policy.objects.create(policy_number="1234") version = Version.objects.create(policy=policy) location = Location.objects.create(version=version) Item.objects.create(version=version, location=location) diff --git a/tests/force_insert_update/tests.py b/tests/force_insert_update/tests.py index 619b8b413d78..9ee444e0ad0d 100644 --- a/tests/force_insert_update/tests.py +++ b/tests/force_insert_update/tests.py @@ -125,7 +125,7 @@ def test_force_insert_false(self): self.assertEqual(obj.value, 3) def test_force_insert_false_with_existing_parent(self): - parent = Counter.objects.create(pk=1, value=1) + parent = Counter.objects.create(value=1) with self.assertNumQueries(2): SubCounter.objects.create(pk=parent.pk, value=2) @@ -152,7 +152,7 @@ def test_force_insert_with_grandparent(self): def test_force_insert_with_existing_grandparent(self): # Force insert only the last child. - grandparent = Counter.objects.create(pk=1, value=1) + grandparent = Counter.objects.create(value=1) with self.assertNumQueries(4): SubSubCounter(pk=grandparent.pk, value=1).save(force_insert=True) # Force insert a parent, and don't force insert a grandparent. diff --git a/tests/forms_tests/tests/tests.py b/tests/forms_tests/tests/tests.py index 253e34976aa5..b1a2a10d1f43 100644 --- a/tests/forms_tests/tests/tests.py +++ b/tests/forms_tests/tests/tests.py @@ -101,45 +101,46 @@ def test_callable_initial_value(self): The initial value for a callable default returning a queryset is the pk. """ - ChoiceOptionModel.objects.create(id=1, name="default") - ChoiceOptionModel.objects.create(id=2, name="option 2") - ChoiceOptionModel.objects.create(id=3, name="option 3") + obj1 = ChoiceOptionModel.objects.create(id=1, name="default") + obj2 = ChoiceOptionModel.objects.create(id=2, name="option 2") + obj3 = ChoiceOptionModel.objects.create(id=3, name="option 3") self.assertHTMLEqual( ChoiceFieldForm().as_p(), - """ + f"""

- +

-

-

-

""", @@ -147,9 +148,9 @@ def test_callable_initial_value(self): def test_initial_instance_value(self): "Initial instances for model fields may also be instances (refs #7287)" - ChoiceOptionModel.objects.create(id=1, name="default") - obj2 = ChoiceOptionModel.objects.create(id=2, name="option 2") - obj3 = ChoiceOptionModel.objects.create(id=3, name="option 3") + obj1 = ChoiceOptionModel.objects.create(name="default") + obj2 = ChoiceOptionModel.objects.create(name="option 2") + obj3 = ChoiceOptionModel.objects.create(name="option 3") self.assertHTMLEqual( ChoiceFieldForm( initial={ @@ -161,44 +162,45 @@ def test_initial_instance_value(self): ), } ).as_p(), - """ + f"""

- +

-

- -

- -

""", diff --git a/tests/model_formsets/tests.py b/tests/model_formsets/tests.py index f5049e0cf7f1..57f22cc52cfd 100644 --- a/tests/model_formsets/tests.py +++ b/tests/model_formsets/tests.py @@ -827,7 +827,7 @@ def test_inline_formsets_with_custom_pk(self): AuthorBooksFormSet2 = inlineformset_factory( Author, BookWithCustomPK, can_delete=False, extra=1, fields="__all__" ) - author = Author.objects.create(pk=1, name="Charles Baudelaire") + author = Author.objects.create(name="Charles Baudelaire") formset = AuthorBooksFormSet2(instance=author) self.assertEqual(len(formset.forms), 1) @@ -840,7 +840,7 @@ def test_inline_formsets_with_custom_pk(self): '' '

', + f'value="{author.pk}" id="id_bookwithcustompk_set-0-author">

', ) data = { @@ -860,7 +860,7 @@ def test_inline_formsets_with_custom_pk(self): saved = formset.save() self.assertEqual(len(saved), 1) (book1,) = saved - self.assertEqual(book1.pk, 77777) + self.assertEqual(book1.pk, Decimal("77777")) book1 = author.bookwithcustompk_set.get() self.assertEqual(book1.title, "Les Fleurs du Mal") @@ -872,7 +872,7 @@ def test_inline_formsets_with_multi_table_inheritance(self): AuthorBooksFormSet3 = inlineformset_factory( Author, AlternateBook, can_delete=False, extra=1, fields="__all__" ) - author = Author.objects.create(pk=1, name="Charles Baudelaire") + author = Author.objects.create(name="Charles Baudelaire") formset = AuthorBooksFormSet3(instance=author) self.assertEqual(len(formset.forms), 1) @@ -884,8 +884,8 @@ def test_inline_formsets_with_multi_table_inheritance(self): '

' '' - '' + '' '

', ) @@ -922,7 +922,7 @@ def test_inline_formsets_with_nullable_unique_together(self): extra=2, fields="__all__", ) - author = Author.objects.create(pk=1, name="Charles Baudelaire") + author = Author.objects.create(name="Charles Baudelaire") data = { # The number of forms rendered. @@ -931,9 +931,9 @@ def test_inline_formsets_with_nullable_unique_together(self): "bookwithoptionalalteditor_set-INITIAL_FORMS": "0", # The max number of forms. "bookwithoptionalalteditor_set-MAX_NUM_FORMS": "", - "bookwithoptionalalteditor_set-0-author": "1", + "bookwithoptionalalteditor_set-0-author": str(author.pk), "bookwithoptionalalteditor_set-0-title": "Les Fleurs du Mal", - "bookwithoptionalalteditor_set-1-author": "1", + "bookwithoptionalalteditor_set-1-author": str(author.pk), "bookwithoptionalalteditor_set-1-title": "Les Fleurs du Mal", } formset = AuthorBooksFormSet4(data, instance=author) @@ -942,21 +942,19 @@ def test_inline_formsets_with_nullable_unique_together(self): saved = formset.save() self.assertEqual(len(saved), 2) book1, book2 = saved - self.assertEqual(book1.author_id, 1) + self.assertEqual(book1.author_id, author.pk) self.assertEqual(book1.title, "Les Fleurs du Mal") - self.assertEqual(book2.author_id, 1) + self.assertEqual(book2.author_id, author.pk) self.assertEqual(book2.title, "Les Fleurs du Mal") def test_inline_formsets_with_custom_save_method(self): AuthorBooksFormSet = inlineformset_factory( Author, Book, can_delete=False, extra=2, fields="__all__" ) - author = Author.objects.create(pk=1, name="Charles Baudelaire") - book1 = Book.objects.create( - pk=1, author=author, title="Les Paradis Artificiels" - ) - book2 = Book.objects.create(pk=2, author=author, title="Les Fleurs du Mal") - book3 = Book.objects.create(pk=3, author=author, title="Flowers of Evil") + author = Author.objects.create(name="Charles Baudelaire") + book1 = Book.objects.create(author=author, title="Les Paradis Artificiels") + book2 = Book.objects.create(author=author, title="Les Fleurs du Mal") + book3 = Book.objects.create(author=author, title="Flowers of Evil") class PoemForm(forms.ModelForm): def save(self, commit=True): @@ -998,9 +996,10 @@ def save(self, commit=True): '

' '' - '' - '' + '' + '' "

", ) self.assertHTMLEqual( @@ -1008,9 +1007,10 @@ def save(self, commit=True): '

' '' - '' - '' + '' + '' "

", ) self.assertHTMLEqual( @@ -1018,18 +1018,18 @@ def save(self, commit=True): '

' '' - '' - '

', + '' + '', ) self.assertHTMLEqual( formset.forms[3].as_p(), '

' '' - '' + '' '

', ) self.assertHTMLEqual( @@ -1037,8 +1037,8 @@ def save(self, commit=True): '

' '' - '' + '' '

', ) @@ -1065,27 +1065,27 @@ def save(self, commit=True): '

' '' - '' - '

', + '' + '

', ) self.assertHTMLEqual( formset.forms[1].as_p(), '

' '' - '' - '

', + '' + f'

', ) self.assertHTMLEqual( formset.forms[2].as_p(), '

' '' - '' + '' '

', ) @@ -1162,7 +1162,7 @@ def test_custom_pk(self): # Custom primary keys with ForeignKey, OneToOneField and AutoField. - place = Place.objects.create(pk=1, name="Giordanos", city="Chicago") + place = Place.objects.create(name="Giordanos", city="Chicago") FormSet = inlineformset_factory( Place, Owner, extra=2, can_delete=False, fields="__all__" @@ -1174,7 +1174,7 @@ def test_custom_pk(self): '

' '' - '' '

', @@ -1184,7 +1184,7 @@ def test_custom_pk(self): '

' '' - '' '

', @@ -1214,7 +1214,7 @@ def test_custom_pk(self): '

' '' - '' '

' % owner1.auto_id, @@ -1224,7 +1224,7 @@ def test_custom_pk(self): '

' '' - '' '

', @@ -1234,7 +1234,7 @@ def test_custom_pk(self): '

' '' - '' '

', @@ -1337,7 +1337,7 @@ def test_custom_pk(self): def test_unique_true_enforces_max_num_one(self): # ForeignKey with unique=True should enforce max_num=1 - place = Place.objects.create(pk=1, name="Giordanos", city="Chicago") + place = Place.objects.create(name="Giordanos", city="Chicago") FormSet = inlineformset_factory( Place, Location, can_delete=False, fields="__all__" @@ -1354,7 +1354,7 @@ def test_unique_true_enforces_max_num_one(self): '

' '' - '' '

', @@ -1761,7 +1761,7 @@ def test_model_formset_with_initial_queryset(self): # has_changed should work with queryset and list of pk's # see #18898 FormSet = modelformset_factory(AuthorMeeting, fields="__all__") - Author.objects.create(pk=1, name="Charles Baudelaire") + Author.objects.create(name="Charles Baudelaire") data = { "form-TOTAL_FORMS": 1, "form-INITIAL_FORMS": 0, @@ -1863,10 +1863,10 @@ def test_prevent_duplicates_from_with_the_same_formset(self): self.assertTrue(formset.is_valid()) FormSet = inlineformset_factory(Author, Book, extra=0, fields="__all__") - author = Author.objects.create(pk=1, name="Charles Baudelaire") - Book.objects.create(pk=1, author=author, title="Les Paradis Artificiels") - Book.objects.create(pk=2, author=author, title="Les Fleurs du Mal") - Book.objects.create(pk=3, author=author, title="Flowers of Evil") + author = Author.objects.create(name="Charles Baudelaire") + Book.objects.create(author=author, title="Les Paradis Artificiels") + Book.objects.create(author=author, title="Les Fleurs du Mal") + Book.objects.create(author=author, title="Flowers of Evil") book_ids = author.book_set.order_by("id").values_list("id", flat=True) data = { @@ -2232,7 +2232,7 @@ def test_inlineformset_factory_help_text_overrides(self): self.assertEqual(form["title"].help_text, "Choose carefully.") def test_modelformset_factory_error_messages_overrides(self): - author = Author.objects.create(pk=1, name="Charles Baudelaire") + author = Author.objects.create(name="Charles Baudelaire") BookFormSet = modelformset_factory( Book, fields="__all__", @@ -2243,7 +2243,7 @@ def test_modelformset_factory_error_messages_overrides(self): self.assertEqual(form.errors, {"title": ["Title too long!!"]}) def test_inlineformset_factory_error_messages_overrides(self): - author = Author.objects.create(pk=1, name="Charles Baudelaire") + author = Author.objects.create(name="Charles Baudelaire") BookFormSet = inlineformset_factory( Author, Book, @@ -2255,7 +2255,7 @@ def test_inlineformset_factory_error_messages_overrides(self): self.assertEqual(form.errors, {"title": ["Title too long!!"]}) def test_modelformset_factory_field_class_overrides(self): - author = Author.objects.create(pk=1, name="Charles Baudelaire") + author = Author.objects.create(name="Charles Baudelaire") BookFormSet = modelformset_factory( Book, fields="__all__", @@ -2268,7 +2268,7 @@ def test_modelformset_factory_field_class_overrides(self): self.assertIsInstance(form.fields["title"], forms.SlugField) def test_inlineformset_factory_field_class_overrides(self): - author = Author.objects.create(pk=1, name="Charles Baudelaire") + author = Author.objects.create(name="Charles Baudelaire") BookFormSet = inlineformset_factory( Author, Book, diff --git a/tests/multiple_database/tests.py b/tests/multiple_database/tests.py index 85091441aa15..42b2ba2486a1 100644 --- a/tests/multiple_database/tests.py +++ b/tests/multiple_database/tests.py @@ -899,8 +899,7 @@ def test_o2o_cross_database_protection(self): new_bob_profile = UserProfile(flavor="spring surprise") - # assigning a profile requires an explicit pk as the object isn't saved - charlie = User(pk=51, username="charlie", email="charlie@example.com") + charlie = User(username="charlie", email="charlie@example.com") charlie.set_unusable_password() # initially, no db assigned @@ -1882,10 +1881,10 @@ def test_m2m_managers(self): managers """ pro = Book.objects.using("other").create( - pk=1, title="Pro Django", published=datetime.date(2008, 12, 16) + title="Pro Django", published=datetime.date(2008, 12, 16) ) - marty = Person.objects.using("other").create(pk=1, name="Marty Alchin") + marty = Person.objects.using("other").create(name="Marty Alchin") self.assertEqual(pro.authors.db, "other") self.assertEqual(pro.authors.db_manager("default").db, "default") @@ -1900,9 +1899,8 @@ def test_foreign_key_managers(self): FK reverse relations are represented by managers, and can be controlled like managers. """ - marty = Person.objects.using("other").create(pk=1, name="Marty Alchin") + marty = Person.objects.using("other").create(name="Marty Alchin") Book.objects.using("other").create( - pk=1, title="Pro Django", published=datetime.date(2008, 12, 16), editor=marty, diff --git a/tests/order_with_respect_to/base_tests.py b/tests/order_with_respect_to/base_tests.py index ec3793411da9..8252398fbbd6 100644 --- a/tests/order_with_respect_to/base_tests.py +++ b/tests/order_with_respect_to/base_tests.py @@ -19,10 +19,10 @@ def setUpTestData(cls): cls.q1 = cls.Question.objects.create( text="Which Beatle starts with the letter 'R'?" ) - cls.Answer.objects.create(text="John", question=cls.q1) - cls.Answer.objects.create(text="Paul", question=cls.q1) - cls.Answer.objects.create(text="George", question=cls.q1) - cls.Answer.objects.create(text="Ringo", question=cls.q1) + cls.a1 = cls.Answer.objects.create(text="John", question=cls.q1) + cls.a2 = cls.Answer.objects.create(text="Paul", question=cls.q1) + cls.a3 = cls.Answer.objects.create(text="George", question=cls.q1) + cls.a4 = cls.Answer.objects.create(text="Ringo", question=cls.q1) def test_default_to_insertion_order(self): # Answers will always be ordered in the order they were inserted. @@ -125,7 +125,9 @@ def db_for_write(self, model, **hints): using="other", ), ): - self.q1.set_answer_order([3, 1, 2, 4]) + self.q1.set_answer_order( + [self.a3.pk, self.a1.pk, self.a2.pk, self.a4.pk] + ) def test_bulk_create_with_empty_parent(self): """ diff --git a/tests/queries/tests.py b/tests/queries/tests.py index 169ca4924af4..71d0b30196fe 100644 --- a/tests/queries/tests.py +++ b/tests/queries/tests.py @@ -129,9 +129,9 @@ def setUpTestData(cls): cls.t4 = Tag.objects.create(name="t4", parent=cls.t3) cls.t5 = Tag.objects.create(name="t5", parent=cls.t3) - cls.n1 = Note.objects.create(note="n1", misc="foo", id=1) - cls.n2 = Note.objects.create(note="n2", misc="bar", id=2) - cls.n3 = Note.objects.create(note="n3", misc="foo", id=3, negate=False) + cls.n1 = Note.objects.create(note="n1", misc="foo") + cls.n2 = Note.objects.create(note="n2", misc="bar") + cls.n3 = Note.objects.create(note="n3", misc="foo", negate=False) cls.ann1 = Annotation.objects.create(name="a1", tag=cls.t1) cls.ann1.notes.add(cls.n1) @@ -707,11 +707,13 @@ def test_ticket4358(self): self.assertIn("note_id", ExtraInfo.objects.values()[0]) # You can also pass it in explicitly. self.assertSequenceEqual( - ExtraInfo.objects.values("note_id"), [{"note_id": 1}, {"note_id": 2}] + ExtraInfo.objects.values("note_id"), + [{"note_id": self.n1.pk}, {"note_id": self.n2.pk}], ) # ...or use the field name. self.assertSequenceEqual( - ExtraInfo.objects.values("note"), [{"note": 1}, {"note": 2}] + ExtraInfo.objects.values("note"), + [{"note": self.n1.pk}, {"note": self.n2.pk}], ) def test_ticket6154(self): @@ -938,7 +940,7 @@ def test_ticket9985(self): # qs.values_list(...).values(...) combinations should work. self.assertSequenceEqual( Note.objects.values_list("note", flat=True).values("id").order_by("id"), - [{"id": 1}, {"id": 2}, {"id": 3}], + [{"id": self.n1.pk}, {"id": self.n2.pk}, {"id": self.n3.pk}], ) self.assertSequenceEqual( Annotation.objects.filter( @@ -1846,8 +1848,8 @@ class Queries5Tests(TestCase): def setUpTestData(cls): # Ordering by 'rank' gives us rank2, rank1, rank3. Ordering by the # Meta.ordering will be rank3, rank2, rank1. - cls.n1 = Note.objects.create(note="n1", misc="foo", id=1) - cls.n2 = Note.objects.create(note="n2", misc="bar", id=2) + cls.n1 = Note.objects.create(note="n1", misc="foo") + cls.n2 = Note.objects.create(note="n2", misc="bar") e1 = ExtraInfo.objects.create(info="e1", note=cls.n1) e2 = ExtraInfo.objects.create(info="e2", note=cls.n2) a1 = Author.objects.create(name="a1", num=1001, extra=e1) @@ -2073,7 +2075,7 @@ def test_join_already_in_query(self): class DisjunctiveFilterTests(TestCase): @classmethod def setUpTestData(cls): - cls.n1 = Note.objects.create(note="n1", misc="foo", id=1) + cls.n1 = Note.objects.create(note="n1", misc="foo") cls.e1 = ExtraInfo.objects.create(info="e1", note=cls.n1) def test_ticket7872(self): @@ -2115,7 +2117,7 @@ def setUpTestData(cls): cls.t3 = Tag.objects.create(name="t3", parent=cls.t1) cls.t4 = Tag.objects.create(name="t4", parent=cls.t3) cls.t5 = Tag.objects.create(name="t5", parent=cls.t3) - n1 = Note.objects.create(note="n1", misc="foo", id=1) + n1 = Note.objects.create(note="n1", misc="foo") cls.ann1 = Annotation.objects.create(name="a1", tag=cls.t1) cls.ann1.notes.add(n1) cls.ann2 = Annotation.objects.create(name="a2", tag=cls.t4) @@ -2242,7 +2244,7 @@ def test_xor_subquery(self): class RawQueriesTests(TestCase): @classmethod def setUpTestData(cls): - Note.objects.create(note="n1", misc="foo", id=1) + Note.objects.create(note="n1", misc="foo") def test_ticket14729(self): # Test representation of raw query with one or few parameters passed as @@ -2273,7 +2275,7 @@ def test_ticket10432(self): class ComparisonTests(TestCase): @classmethod def setUpTestData(cls): - cls.n1 = Note.objects.create(note="n1", misc="foo", id=1) + cls.n1 = Note.objects.create(note="n1", misc="foo") e1 = ExtraInfo.objects.create(info="e1", note=cls.n1) cls.a2 = Author.objects.create(name="a2", num=2002, extra=e1) @@ -2916,7 +2918,7 @@ def test_slicing_can_slice_again_after_slicing(self): def test_slicing_cannot_filter_queryset_once_sliced(self): msg = "Cannot filter a query once a slice has been taken." with self.assertRaisesMessage(TypeError, msg): - Article.objects.all()[0:5].filter(id=1) + Article.objects.all()[0:5].filter(name="foo") def test_slicing_cannot_reorder_queryset_once_sliced(self): msg = "Cannot reorder a query once a slice has been taken." @@ -3408,7 +3410,7 @@ class ExcludeTest17600(TestCase): @classmethod def setUpTestData(cls): - # Create a few Orders. + # Create a few Orders. Explicit pks needed for IntegerField pk. cls.o1 = Order.objects.create(pk=1) cls.o2 = Order.objects.create(pk=2) cls.o3 = Order.objects.create(pk=3) diff --git a/tests/sites_framework/tests.py b/tests/sites_framework/tests.py index 4a297a924349..33c3b354784b 100644 --- a/tests/sites_framework/tests.py +++ b/tests/sites_framework/tests.py @@ -1,4 +1,3 @@ -from django.conf import settings from django.contrib.sites.managers import CurrentSiteManager from django.contrib.sites.models import Site from django.core import checks @@ -12,31 +11,27 @@ class SitesFrameworkTestCase(TestCase): @classmethod def setUpTestData(cls): - Site.objects.get_or_create( - id=settings.SITE_ID, domain="example.com", name="example.com" - ) - Site.objects.create( - id=settings.SITE_ID + 1, domain="example2.com", name="example2.com" - ) + cls.site1 = Site.objects.get(domain="example.com", name="example.com") + cls.site2 = Site.objects.create(domain="example2.com", name="example2.com") def test_site_fk(self): article = ExclusiveArticle.objects.create( - title="Breaking News!", site_id=settings.SITE_ID + title="Breaking News!", site=self.site1 ) self.assertEqual(ExclusiveArticle.on_site.get(), article) def test_sites_m2m(self): article = SyndicatedArticle.objects.create(title="Fresh News!") - article.sites.add(Site.objects.get(id=settings.SITE_ID)) - article.sites.add(Site.objects.get(id=settings.SITE_ID + 1)) + article.sites.add(self.site1) + article.sites.add(self.site2) article2 = SyndicatedArticle.objects.create(title="More News!") - article2.sites.add(Site.objects.get(id=settings.SITE_ID + 1)) + article2.sites.add(self.site2) self.assertEqual(SyndicatedArticle.on_site.get(), article) def test_custom_named_field(self): article = CustomArticle.objects.create( title="Tantalizing News!", - places_this_article_should_appear_id=settings.SITE_ID, + places_this_article_should_appear_id=self.site1.id, ) self.assertEqual(CustomArticle.on_site.get(), article) From a225e90e58c65dafd5b15e1e5ff37dd038b1dd97 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Mon, 25 May 2026 18:15:09 -0400 Subject: [PATCH 2/3] Added DatabaseOperations.get_hardcoded_pk(). --- django/db/backends/base/operations.py | 11 ++++++++ tests/admin_changelist/tests.py | 13 ++++++---- tests/admin_inlines/tests.py | 12 +++++++-- tests/admin_views/test_actions.py | 4 +-- tests/admin_views/tests.py | 10 +++++--- tests/admin_widgets/models.py | 8 ++++-- tests/aggregation/tests.py | 3 ++- tests/auth_tests/test_management.py | 8 +++--- tests/auth_tests/test_views.py | 3 ++- tests/auth_tests/urls_custom_user_admin.py | 3 ++- tests/basic/tests.py | 10 +++++--- tests/force_insert_update/tests.py | 29 ++++++++++++---------- tests/forms_tests/models.py | 6 ++--- tests/forms_tests/tests/tests.py | 9 ++++--- tests/generic_views/test_edit.py | 3 ++- tests/generic_views/views.py | 3 ++- tests/inline_formsets/tests.py | 3 ++- tests/model_fields/models.py | 2 +- tests/model_fields/test_foreignkey.py | 6 +++-- tests/prefetch_related/tests.py | 5 ++-- tests/queries/test_bulk_update.py | 2 +- tests/queries/tests.py | 4 +-- tests/queryset_pickle/tests.py | 12 +++++---- tests/sites_tests/tests.py | 5 ++-- tests/validation/test_unique.py | 6 +++-- tests/validation/tests.py | 5 +++- 26 files changed, 118 insertions(+), 67 deletions(-) diff --git a/django/db/backends/base/operations.py b/django/db/backends/base/operations.py index 29ef7d93d166..42b66660080e 100644 --- a/django/db/backends/base/operations.py +++ b/django/db/backends/base/operations.py @@ -886,3 +886,14 @@ def compile_json_path(self, key_transforms, include_root=True): ) path.append(self.format_json_path_numeric_index(num)) return "".join(path) + + def get_hardcoded_pk(self, value): + """ + Given an integer suggestion (which works for built-in backends because + they don't override DEFAULT_AUTO_FIELD), return a hardcoded primary key + value for use in tests. + + Example use case: MongoDB uses DEFAULT_AUTO_FIELD=ObjectIdAutoField and + an integer pk can be converted to bson.ObjectId(). + """ + return value diff --git a/tests/admin_changelist/tests.py b/tests/admin_changelist/tests.py index f1d1abc0b0a5..ed1e0f215fdd 100644 --- a/tests/admin_changelist/tests.py +++ b/tests/admin_changelist/tests.py @@ -996,7 +996,8 @@ def test_no_distinct_for_m2m_in_list_filter_without_params(self): self.assertIs(cl.queryset.query.distinct, False) # A ManyToManyField in params does have distinct applied. - request = self.factory.get("/band/", {"genres": "0"}) + pk_value = str(connection.ops.get_hardcoded_pk(0)) + request = self.factory.get("/band/", {"genres": pk_value}) request.user = self.superuser cl = m.get_changelist_instance(request) self.assertIs(cl.queryset.query.distinct, True) @@ -1473,9 +1474,10 @@ def test_deterministic_order_for_unordered_model(self): default ordering defined (#17198). """ superuser = self._create_superuser("superuser") + pk = connection.ops.get_hardcoded_pk for counter in range(1, 51): - UnorderedObject.objects.create(id=counter, bool=True) + UnorderedObject.objects.create(id=pk(counter), bool=True) class UnorderedObjectAdmin(admin.ModelAdmin): list_per_page = 10 @@ -1491,7 +1493,7 @@ def check_results_order(ascending=False): response = model_admin.changelist_view(request) for result in response.context_data["cl"].result_list: counter += 1 if ascending else -1 - self.assertEqual(result.id, counter) + self.assertEqual(result.id, pk(counter)) custom_site.unregister(UnorderedObject) # When no order is defined at all, everything is ordered by '-pk'. @@ -1534,9 +1536,10 @@ def test_deterministic_order_for_model_ordered_by_its_manager(self): defines a default ordering (#17198). """ superuser = self._create_superuser("superuser") + pk = connection.ops.get_hardcoded_pk for counter in range(1, 51): - OrderedObject.objects.create(id=counter, bool=True, number=counter) + OrderedObject.objects.create(id=pk(counter), bool=True, number=counter) class OrderedObjectAdmin(admin.ModelAdmin): list_per_page = 10 @@ -1552,7 +1555,7 @@ def check_results_order(ascending=False): response = model_admin.changelist_view(request) for result in response.context_data["cl"].result_list: counter += 1 if ascending else -1 - self.assertEqual(result.id, counter) + self.assertEqual(result.id, pk(counter)) custom_site.unregister(OrderedObject) # When no order is defined at all, use the model's default ordering diff --git a/tests/admin_inlines/tests.py b/tests/admin_inlines/tests.py index 62e97e317a54..6e2ee695ec96 100644 --- a/tests/admin_inlines/tests.py +++ b/tests/admin_inlines/tests.py @@ -3,6 +3,7 @@ from django.contrib.admin.tests import AdminSeleniumTestCase from django.contrib.auth.models import Permission, User from django.contrib.contenttypes.models import ContentType +from django.db import connection from django.test import RequestFactory, TestCase, override_settings from django.test.selenium import screenshot_cases from django.urls import reverse @@ -525,8 +526,15 @@ def test_localize_pk_shortcut(self): The "View on Site" link is correct for locales that use thousand separators. """ - holder = Holder.objects.create(pk=123456789, dummy=42) - inner = Inner.objects.create(pk=987654321, holder=holder, dummy=42, readonly="") + holder = Holder.objects.create( + pk=connection.ops.get_hardcoded_pk(123456789), dummy=42 + ) + inner = Inner.objects.create( + pk=connection.ops.get_hardcoded_pk(987654321), + holder=holder, + dummy=42, + readonly="", + ) response = self.client.get( reverse("admin:admin_inlines_holder_change", args=(holder.id,)) ) diff --git a/tests/admin_views/test_actions.py b/tests/admin_views/test_actions.py index dbed3a33b6f0..d82c8f3f5fc1 100644 --- a/tests/admin_views/test_actions.py +++ b/tests/admin_views/test_actions.py @@ -123,7 +123,7 @@ def test_non_localized_pk(self): If USE_THOUSAND_SEPARATOR is set, the ids for the objects selected for deletion are rendered without separators. """ - s = ExternalSubscriber.objects.create(id=9999) + s = ExternalSubscriber.objects.create(id=connection.ops.get_hardcoded_pk(9999)) action_data = { ACTION_CHECKBOX_NAME: [s.pk, self.s2.pk], "action": "delete_selected", @@ -133,7 +133,7 @@ def test_non_localized_pk(self): reverse("admin:admin_views_subscriber_changelist"), action_data ) self.assertTemplateUsed(response, "admin/delete_selected_confirmation.html") - self.assertContains(response, 'value="9999"') # Instead of 9,999 + self.assertContains(response, f'value="{s.pk}"') # Instead of 9,999 self.assertContains(response, 'value="%s"' % self.s2.pk) def test_model_admin_default_delete_action_protected(self): diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index eb43194bcc1b..64e7985a2333 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -26,6 +26,7 @@ from django.core import mail from django.core.checks import Error from django.core.files import temp as tempfile +from django.db import connection from django.db.models.utils import get_blank_choice_label from django.forms.utils import ErrorList from django.template.response import TemplateResponse @@ -3985,8 +3986,9 @@ def setUpTestData(cls): cls.ssh1 = SuperSecretHideout.objects.create( location="super floating castle!", supervillain=cls.sv1 ) - cls.cy1 = CyclicOne.objects.create(pk=1, name="I am recursive", two_id=1) - cls.cy2 = CyclicTwo.objects.create(pk=1, name="I am recursive too", one_id=1) + pk = connection.ops.get_hardcoded_pk(1) + cls.cy1 = CyclicOne.objects.create(pk=pk, name="I am recursive", two_id=pk) + cls.cy2 = CyclicTwo.objects.create(pk=pk, name="I am recursive too", one_id=pk) def setUp(self): self.client.force_login(self.superuser) @@ -8830,7 +8832,7 @@ def send_message(self, level): message with the level has appeared in the response. """ action_data = { - ACTION_CHECKBOX_NAME: [1], + ACTION_CHECKBOX_NAME: [connection.ops.get_hardcoded_pk(1)], "action": "message_%s" % level, "index": 0, } @@ -8862,7 +8864,7 @@ def test_message_error(self): def test_message_extra_tags(self): action_data = { - ACTION_CHECKBOX_NAME: [1], + ACTION_CHECKBOX_NAME: [connection.ops.get_hardcoded_pk(1)], "action": "message_extra_tags", "index": 0, } diff --git a/tests/admin_widgets/models.py b/tests/admin_widgets/models.py index 0113ecb7c8ad..8f1b9f4f8c7b 100644 --- a/tests/admin_widgets/models.py +++ b/tests/admin_widgets/models.py @@ -3,7 +3,7 @@ from django.contrib.auth.models import User from django.core.files.storage import FileSystemStorage -from django.db import models +from django.db import connection, models try: from PIL import Image @@ -104,11 +104,15 @@ def __str__(self): return self.name +def get_event_choices(): + return models.Q(pk__gt=connection.ops.get_hardcoded_pk(0)) + + class Event(models.Model): main_band = models.ForeignKey( Band, models.CASCADE, - limit_choices_to=models.Q(pk__gt=0), + limit_choices_to=get_event_choices, related_name="events_main_band_at", ) supporting_bands = models.ManyToManyField( diff --git a/tests/aggregation/tests.py b/tests/aggregation/tests.py index af46e616095b..ae9a2b88c797 100644 --- a/tests/aggregation/tests.py +++ b/tests/aggregation/tests.py @@ -2849,9 +2849,10 @@ def test_aggregate_reference_lookup_rhs(self): self.assertEqual(aggregates, {"count": 1}) def test_aggregate_reference_lookup_rhs_iter(self): + zero = connection.ops.get_hardcoded_pk(0) aggregates = Author.objects.annotate( max_book_author=Max("book__authors"), - ).aggregate(count=Count("id", filter=Q(id__in=[F("max_book_author"), 0]))) + ).aggregate(count=Count("id", filter=Q(id__in=[F("max_book_author"), zero]))) self.assertEqual(aggregates, {"count": 1}) @skipUnlessDBFeature("supports_select_union") diff --git a/tests/auth_tests/test_management.py b/tests/auth_tests/test_management.py index 985dfd79f8e7..754840368c57 100644 --- a/tests/auth_tests/test_management.py +++ b/tests/auth_tests/test_management.py @@ -18,7 +18,7 @@ from django.contrib.contenttypes.models import ContentType from django.core.management import call_command from django.core.management.base import CommandError -from django.db import migrations +from django.db import connection, migrations from django.test import TestCase, override_settings from django.test.testcases import TransactionTestCase from django.utils.translation import gettext_lazy as _ @@ -611,7 +611,7 @@ def test(self): def test_validate_fk(self): email = Email.objects.create(email="mymail@gmail.com") Group.objects.all().delete() - nonexistent_group_id = 1 + nonexistent_group_id = connection.ops.get_hardcoded_pk(1) msg = f"group instance with id {nonexistent_group_id!r} is not a valid choice." with self.assertRaisesMessage(CommandError, msg): @@ -628,7 +628,7 @@ def test_validate_fk(self): def test_validate_fk_environment_variable(self): email = Email.objects.create(email="mymail@gmail.com") Group.objects.all().delete() - nonexistent_group_id = 1 + nonexistent_group_id = connection.ops.get_hardcoded_pk(1) msg = f"group instance with id {nonexistent_group_id!r} is not a valid choice." with mock.patch.dict( @@ -648,7 +648,7 @@ def test_validate_fk_environment_variable(self): def test_validate_fk_via_option_interactive(self): email = Email.objects.create(email="mymail@gmail.com") Group.objects.all().delete() - nonexistent_group_id = 1 + nonexistent_group_id = connection.ops.get_hardcoded_pk(1) msg = f"group instance with id {nonexistent_group_id!r} is not a valid choice." @mock_inputs( diff --git a/tests/auth_tests/test_views.py b/tests/auth_tests/test_views.py index 9baa6ddb5fb6..a8cd3a0b205c 100644 --- a/tests/auth_tests/test_views.py +++ b/tests/auth_tests/test_views.py @@ -1780,7 +1780,8 @@ def test_admin_password_change(self): ) self.assertRedirects(response, user_change_url) row = LogEntry.objects.latest("id") - self.assertEqual(row.user_id, 1) # hardcoded in CustomUserAdmin.log_change() + # hardcoded in CustomUserAdmin.log_change() + self.assertEqual(row.user_id, connection.ops.get_hardcoded_pk(1)) self.assertEqual(row.object_id, str(u.pk)) self.assertEqual(row.get_change_message(), "Changed password.") diff --git a/tests/auth_tests/urls_custom_user_admin.py b/tests/auth_tests/urls_custom_user_admin.py index 1c7ce1eb420d..d0f393875f2a 100644 --- a/tests/auth_tests/urls_custom_user_admin.py +++ b/tests/auth_tests/urls_custom_user_admin.py @@ -1,6 +1,7 @@ from django.contrib import admin from django.contrib.auth import get_user_model from django.contrib.auth.admin import UserAdmin +from django.db import connection from django.urls import path site = admin.AdminSite(name="custom_user_admin") @@ -11,7 +12,7 @@ def log_change(self, request, obj, message): # LogEntry.user column doesn't get altered to expect a UUID, so set an # integer manually to avoid causing an error. original_pk = request.user.pk - request.user.pk = 1 + request.user.pk = connection.ops.get_hardcoded_pk(1) super().log_change(request, obj, message) request.user.pk = original_pk diff --git a/tests/basic/tests.py b/tests/basic/tests.py index ed655833e271..034a7548da69 100644 --- a/tests/basic/tests.py +++ b/tests/basic/tests.py @@ -220,8 +220,9 @@ def test_auto_field_with_value_refreshed(self): An auto field must be refreshed by Model.save() even when a value is set because the database may return a value of a different type. """ - a = Article.objects.create(pk="123456", pub_date=datetime(2025, 9, 16)) - self.assertEqual(a.pk, 123456) + pk = connection.ops.get_hardcoded_pk(123456) + a = Article.objects.create(pk=str(pk), pub_date=datetime(2025, 9, 16)) + self.assertEqual(a.pk, pk) class ModelTest(TestCase): @@ -273,13 +274,14 @@ def test_microsecond_precision(self): def test_manually_specify_primary_key(self): # You can manually specify the primary key when creating a new object. + pk = connection.ops.get_hardcoded_pk(101) a101 = Article( - id=101, + id=pk, headline="Article 101", pub_date=datetime(2005, 7, 31, 12, 30, 45), ) a101.save() - a101 = Article.objects.get(pk=101) + a101 = Article.objects.get(pk=pk) self.assertEqual(a101.headline, "Article 101") def test_create_method(self): diff --git a/tests/force_insert_update/tests.py b/tests/force_insert_update/tests.py index 9ee444e0ad0d..8bb697a3530b 100644 --- a/tests/force_insert_update/tests.py +++ b/tests/force_insert_update/tests.py @@ -1,5 +1,5 @@ from django.core.exceptions import ObjectNotUpdated -from django.db import DatabaseError, IntegrityError, models, transaction +from django.db import DatabaseError, IntegrityError, connection, models, transaction from django.test import TestCase from .models import ( @@ -130,25 +130,27 @@ def test_force_insert_false_with_existing_parent(self): SubCounter.objects.create(pk=parent.pk, value=2) def test_force_insert_parent(self): + pk = connection.ops.get_hardcoded_pk with self.assertNumQueries(3): - SubCounter(pk=1, value=1).save(force_insert=True) + SubCounter(pk=pk(1), value=1).save(force_insert=True) # Force insert a new parent and don't UPDATE first. with self.assertNumQueries(2): - SubCounter(pk=2, value=1).save(force_insert=(Counter,)) + SubCounter(pk=pk(2), value=1).save(force_insert=(Counter,)) with self.assertNumQueries(2): - SubCounter(pk=3, value=1).save(force_insert=(models.Model,)) + SubCounter(pk=pk(3), value=1).save(force_insert=(models.Model,)) def test_force_insert_with_grandparent(self): + pk = connection.ops.get_hardcoded_pk with self.assertNumQueries(4): - SubSubCounter(pk=1, value=1).save(force_insert=True) + SubSubCounter(pk=pk(1), value=1).save(force_insert=True) # Force insert parents on all levels and don't UPDATE first. with self.assertNumQueries(3): - SubSubCounter(pk=2, value=1).save(force_insert=(models.Model,)) + SubSubCounter(pk=pk(2), value=1).save(force_insert=(models.Model,)) with self.assertNumQueries(3): - SubSubCounter(pk=3, value=1).save(force_insert=(Counter,)) + SubSubCounter(pk=pk(3), value=1).save(force_insert=(Counter,)) # Force insert only the last parent. with self.assertNumQueries(4): - SubSubCounter(pk=4, value=1).save(force_insert=(SubCounter,)) + SubSubCounter(pk=pk(4), value=1).save(force_insert=(SubCounter,)) def test_force_insert_with_existing_grandparent(self): # Force insert only the last child. @@ -165,25 +167,26 @@ def test_force_insert_with_existing_grandparent(self): SubSubCounter(pk=grandparent.pk, value=1).save(force_insert=(Counter,)) def test_force_insert_diamond_mti(self): + pk = connection.ops.get_hardcoded_pk # Force insert all parents. with self.assertNumQueries(4): - DiamondSubSubCounter(pk=1, value=1).save( + DiamondSubSubCounter(pk=pk(1), value=1).save( force_insert=(Counter, SubCounter, OtherSubCounter) ) with self.assertNumQueries(4): - DiamondSubSubCounter(pk=2, value=1).save(force_insert=(models.Model,)) + DiamondSubSubCounter(pk=pk(2), value=1).save(force_insert=(models.Model,)) # Force insert parents, and don't force insert a common grandparent. with self.assertNumQueries(5): - DiamondSubSubCounter(pk=3, value=1).save( + DiamondSubSubCounter(pk=pk(3), value=1).save( force_insert=(SubCounter, OtherSubCounter) ) - grandparent = Counter.objects.create(pk=4, value=1) + grandparent = Counter.objects.create(pk=pk(4), value=1) with self.assertNumQueries(4): DiamondSubSubCounter(pk=grandparent.pk, value=1).save( force_insert=(SubCounter, OtherSubCounter), ) # Force insert all parents, grandparent conflicts. - grandparent = Counter.objects.create(pk=5, value=1) + grandparent = Counter.objects.create(pk=pk(5), value=1) with self.assertRaises(IntegrityError), transaction.atomic(): DiamondSubSubCounter(pk=grandparent.pk, value=1).save( force_insert=(models.Model,) diff --git a/tests/forms_tests/models.py b/tests/forms_tests/models.py index b1319abe17f1..ce349050033b 100644 --- a/tests/forms_tests/models.py +++ b/tests/forms_tests/models.py @@ -3,7 +3,7 @@ import tempfile from django.core.files.storage import FileSystemStorage -from django.db import models +from django.db import connection, models callable_default_counter = itertools.count() @@ -80,11 +80,11 @@ def choice_default_list(): def int_default(): - return 1 + return connection.ops.get_hardcoded_pk(1) def int_list_default(): - return [1] + return [connection.ops.get_hardcoded_pk(1)] class ChoiceFieldModel(models.Model): diff --git a/tests/forms_tests/tests/tests.py b/tests/forms_tests/tests/tests.py index b1a2a10d1f43..5d6fa441b57d 100644 --- a/tests/forms_tests/tests/tests.py +++ b/tests/forms_tests/tests/tests.py @@ -1,7 +1,7 @@ import datetime from django.core.files.uploadedfile import SimpleUploadedFile -from django.db import models +from django.db import connection, models from django.forms import CharField, FileField, Form, ModelForm from django.forms.models import ModelFormMetaclass from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature @@ -101,9 +101,10 @@ def test_callable_initial_value(self): The initial value for a callable default returning a queryset is the pk. """ - obj1 = ChoiceOptionModel.objects.create(id=1, name="default") - obj2 = ChoiceOptionModel.objects.create(id=2, name="option 2") - obj3 = ChoiceOptionModel.objects.create(id=3, name="option 3") + pk = connection.ops.get_hardcoded_pk + obj1 = ChoiceOptionModel.objects.create(id=pk(1), name="default") + obj2 = ChoiceOptionModel.objects.create(id=pk(2), name="option 2") + obj3 = ChoiceOptionModel.objects.create(id=pk(3), name="option 3") self.assertHTMLEqual( ChoiceFieldForm().as_p(), f""" diff --git a/tests/generic_views/test_edit.py b/tests/generic_views/test_edit.py index b483ea1028ff..db8aa64ce0d2 100644 --- a/tests/generic_views/test_edit.py +++ b/tests/generic_views/test_edit.py @@ -1,5 +1,6 @@ from django import forms from django.core.exceptions import ImproperlyConfigured +from django.db import connection from django.test import SimpleTestCase, TestCase, override_settings from django.test.client import RequestFactory from django.urls import reverse @@ -239,7 +240,7 @@ class UpdateViewTests(TestCase): @classmethod def setUpTestData(cls): cls.author = Author.objects.create( - pk=1, # Required for OneAuthorUpdate. + pk=connection.ops.get_hardcoded_pk(1), # Required for OneAuthorUpdate. name="Randall Munroe", slug="randall-munroe", ) diff --git a/tests/generic_views/views.py b/tests/generic_views/views.py index 5348c6763248..29ca0fd0e0bc 100644 --- a/tests/generic_views/views.py +++ b/tests/generic_views/views.py @@ -1,5 +1,6 @@ from django.contrib.auth.decorators import login_required from django.core.paginator import Paginator +from django.db import connection from django.urls import reverse, reverse_lazy from django.utils.decorators import method_decorator from django.views import generic @@ -169,7 +170,7 @@ class OneAuthorUpdate(generic.UpdateView): fields = "__all__" def get_object(self): - return Author.objects.get(pk=1) + return Author.objects.get(pk=connection.ops.get_hardcoded_pk(1)) class SpecializedAuthorUpdate(generic.UpdateView): diff --git a/tests/inline_formsets/tests.py b/tests/inline_formsets/tests.py index eaabc350b4b4..de53a6474298 100644 --- a/tests/inline_formsets/tests.py +++ b/tests/inline_formsets/tests.py @@ -1,3 +1,4 @@ +from django.db import connection from django.forms.models import ModelForm, inlineformset_factory from django.test import TestCase, skipUnlessDBFeature @@ -162,7 +163,7 @@ def test_any_iterable_allowed_as_argument_to_exclude(self): @skipUnlessDBFeature("allows_auto_pk_0") def test_zero_primary_key(self): # Regression test for #21472 - poet = Poet.objects.create(id=0, name="test") + poet = Poet.objects.create(id=connection.ops.get_hardcoded_pk(0), name="test") poet.poem_set.create(name="test poem") PoemFormSet = inlineformset_factory(Poet, Poem, fields="__all__", extra=0) formset = PoemFormSet(None, instance=poet) diff --git a/tests/model_fields/models.py b/tests/model_fields/models.py index c1e356bb5318..9d018e6904a9 100644 --- a/tests/model_fields/models.py +++ b/tests/model_fields/models.py @@ -36,7 +36,7 @@ class Foo(models.Model): def get_foo(): - return Foo.objects.get(id=1).pk + return Foo.objects.get(id=connection.ops.get_hardcoded_pk(1)).pk class Bar(models.Model): diff --git a/tests/model_fields/test_foreignkey.py b/tests/model_fields/test_foreignkey.py index a662a6a1bfdf..2e69d48775c5 100644 --- a/tests/model_fields/test_foreignkey.py +++ b/tests/model_fields/test_foreignkey.py @@ -3,7 +3,7 @@ from django.apps import apps from django.core import checks from django.core.exceptions import FieldError -from django.db import models +from django.db import connection, models from django.test import TestCase, skipIfDBFeature from django.test.utils import isolate_apps @@ -13,7 +13,9 @@ class ForeignKeyTests(TestCase): def test_callable_default(self): """A lazy callable may be used for ForeignKey.default.""" - a = Foo.objects.create(id=1, a="abc", d=Decimal("12.34")) + a = Foo.objects.create( + id=connection.ops.get_hardcoded_pk(1), a="abc", d=Decimal("12.34") + ) b = Bar.objects.create(b="bcd") self.assertEqual(b.a, a) diff --git a/tests/prefetch_related/tests.py b/tests/prefetch_related/tests.py index bb6417b8aecd..cc3034b63333 100644 --- a/tests/prefetch_related/tests.py +++ b/tests/prefetch_related/tests.py @@ -1749,15 +1749,16 @@ def test_using_is_honored_custom_qs(self): class Ticket19607Tests(TestCase): @classmethod def setUpTestData(cls): + pk = connection.ops.get_hardcoded_pk LessonEntry.objects.bulk_create( - LessonEntry(id=id_, name1=name1, name2=name2) + LessonEntry(id=pk(id_), name1=name1, name2=name2) for id_, name1, name2 in [ (1, "einfach", "simple"), (2, "schwierig", "difficult"), ] ) WordEntry.objects.bulk_create( - WordEntry(id=id_, lesson_entry_id=lesson_entry_id, name=name) + WordEntry(id=pk(id_), lesson_entry_id=lesson_entry_id, name=name) for id_, lesson_entry_id, name in [ (1, 1, "einfach"), (2, 1, "simple"), diff --git a/tests/queries/test_bulk_update.py b/tests/queries/test_bulk_update.py index a9025bddccde..9970a5b17ea1 100644 --- a/tests/queries/test_bulk_update.py +++ b/tests/queries/test_bulk_update.py @@ -213,7 +213,7 @@ def test_custom_pk(self): ) def test_falsey_pk_value(self): - order = Order.objects.create(pk=0, name="test") + order = Order.objects.create(pk=connection.ops.get_hardcoded_pk(0), name="test") order.name = "updated" Order.objects.bulk_update([order], ["name"]) order.refresh_from_db() diff --git a/tests/queries/tests.py b/tests/queries/tests.py index 71d0b30196fe..84b347dd1fe8 100644 --- a/tests/queries/tests.py +++ b/tests/queries/tests.py @@ -552,7 +552,7 @@ def test_ticket2091(self): self.assertSequenceEqual(Item.objects.filter(tags__in=[t]), [self.i4]) def test_avoid_infinite_loop_on_too_many_subqueries(self): - x = Tag.objects.filter(pk=1) + x = Tag.objects.filter(pk=connection.ops.get_hardcoded_pk(1)) local_recursion_limit = sys.getrecursionlimit() // 16 msg = "Maximum recursion depth exceeded: too many subqueries." with self.assertRaisesMessage(RecursionError, msg): @@ -560,7 +560,7 @@ def test_avoid_infinite_loop_on_too_many_subqueries(self): x = Tag.objects.filter(pk__in=x) def test_reasonable_number_of_subq_aliases(self): - x = Tag.objects.filter(pk=1) + x = Tag.objects.filter(pk=connection.ops.get_hardcoded_pk(1)) for _ in range(20): x = Tag.objects.filter(pk__in=x) self.assertEqual( diff --git a/tests/queryset_pickle/tests.py b/tests/queryset_pickle/tests.py index e1e4c9a2f877..8422f983f406 100644 --- a/tests/queryset_pickle/tests.py +++ b/tests/queryset_pickle/tests.py @@ -2,7 +2,7 @@ import pickle import django -from django.db import models +from django.db import connection, models from django.test import TestCase from .models import ( @@ -50,7 +50,8 @@ def test_staticmethod_as_default(self): self.assert_pickles(Happening.objects.filter(number2=1)) def test_filter_reverse_fk(self): - self.assert_pickles(Group.objects.filter(event=1)) + event_id = connection.ops.get_hardcoded_pk(1) + self.assert_pickles(Group.objects.filter(event=event_id)) def test_doesnotexist_exception(self): # Ticket #17776 @@ -93,7 +94,7 @@ def test_model_pickle(self): """ A model not defined on module level is picklable. """ - original = Container.SomeModel(pk=1) + original = Container.SomeModel(pk=connection.ops.get_hardcoded_pk(1)) dumped = pickle.dumps(original) reloaded = pickle.loads(dumped) self.assertEqual(original, reloaded) @@ -172,7 +173,8 @@ def test_pickle_prefetch_queryset_still_usable(self): models.Prefetch("event_set", queryset=Event.objects.order_by("id")) ) groups2 = pickle.loads(pickle.dumps(groups)) - self.assertSequenceEqual(groups2.filter(id__gte=0), [g]) + zero = connection.ops.get_hardcoded_pk(0) + self.assertSequenceEqual(groups2.filter(id__gte=zero), [g]) def test_pickle_prefetch_queryset_not_evaluated(self): Group.objects.create(name="foo") @@ -323,7 +325,7 @@ def test_annotation_values_list(self): def test_filter_deferred(self): qs = Happening.objects.all() qs._defer_next_filter = True - qs = qs.filter(id=0) + qs = qs.filter(id=connection.ops.get_hardcoded_pk(0)) self.assert_pickles(qs) def test_missing_django_version_unpickling(self): diff --git a/tests/sites_tests/tests.py b/tests/sites_tests/tests.py index 32bc8f65f9cb..bba486cf1c30 100644 --- a/tests/sites_tests/tests.py +++ b/tests/sites_tests/tests.py @@ -10,6 +10,7 @@ from django.contrib.sites.shortcuts import get_current_site from django.core import checks from django.core.exceptions import ObjectDoesNotExist, ValidationError +from django.db import connection from django.db.models.signals import post_migrate from django.http import HttpRequest, HttpResponse from django.test import SimpleTestCase, TestCase, modify_settings, override_settings @@ -203,7 +204,7 @@ def test_site_natural_key(self): self.assertEqual(Site.objects.get_by_natural_key(self.site.domain), self.site) self.assertEqual(self.site.natural_key(), (self.site.domain,)) - @override_settings(SITE_ID="1") + @override_settings(SITE_ID=str(connection.ops.get_hardcoded_pk(1))) def test_check_site_id_incorrect_type(self): self.assertEqual( check_site_id(None), @@ -229,7 +230,7 @@ def test_check_site_id_failed_to_validate(self): ) def test_valid_site_id(self): - for site_id in [1, None]: + for site_id in [connection.ops.get_hardcoded_pk(1), None]: with self.subTest(site_id=site_id), self.settings(SITE_ID=site_id): self.assertEqual(check_site_id(None), []) diff --git a/tests/validation/test_unique.py b/tests/validation/test_unique.py index 36ee6e9da0cb..7dd66638c526 100644 --- a/tests/validation/test_unique.py +++ b/tests/validation/test_unique.py @@ -3,7 +3,7 @@ from django.apps.registry import Apps from django.core.exceptions import ValidationError -from django.db import models +from django.db import connection, models from django.test import TestCase from .models import ( @@ -136,7 +136,9 @@ def test_primary_key_unique_check_not_performed_when_adding_and_pk_not_specified def test_primary_key_unique_check_performed_when_adding_and_pk_specified(self): # Regression test for #12560 with self.assertNumQueries(1): - mtv = ModelToValidate(number=10, name="Some Name", id=123) + mtv = ModelToValidate( + number=10, name="Some Name", id=connection.ops.get_hardcoded_pk(123) + ) setattr(mtv, "_adding", True) mtv.full_clean() diff --git a/tests/validation/tests.py b/tests/validation/tests.py index 494310e55340..4414c56a5583 100644 --- a/tests/validation/tests.py +++ b/tests/validation/tests.py @@ -1,5 +1,6 @@ from django import forms from django.core.exceptions import NON_FIELD_ERRORS +from django.db import connection from django.test import TestCase from django.utils.functional import lazy @@ -27,7 +28,9 @@ def test_custom_validate_method(self): self.assertFailsValidation(mtv.full_clean, [NON_FIELD_ERRORS, "name"]) def test_wrong_FK_value_raises_error(self): - mtv = ModelToValidate(number=10, name="Some Name", parent_id=3) + mtv = ModelToValidate( + number=10, name="Some Name", parent_id=connection.ops.get_hardcoded_pk(3) + ) self.assertFieldFailsValidationWithMessage( mtv.full_clean, "parent", From c7a55129df3316bb3d31df24dd35c77bd859423a Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Tue, 26 May 2026 09:50:40 -0400 Subject: [PATCH 3/3] Added DatabaseOperations.get_nonexistent_pk(). --- django/db/backends/base/operations.py | 11 +++++++++ tests/admin_changelist/tests.py | 3 ++- tests/admin_views/admin.py | 5 ++-- tests/admin_views/test_actions.py | 5 ++-- tests/aggregation_regress/tests.py | 3 ++- tests/async/test_async_queryset.py | 2 +- tests/basic/tests.py | 2 +- .../forms_tests/tests/test_error_messages.py | 18 ++++++++++----- tests/lookup/tests.py | 4 +++- tests/model_forms/tests.py | 14 +++++------ tests/model_formsets/tests.py | 9 ++++---- tests/model_formsets_regress/tests.py | 12 ++++++---- tests/model_inheritance/tests.py | 2 +- tests/model_inheritance_regress/tests.py | 10 ++++++-- tests/multiple_database/tests.py | 23 +++++++++++++------ tests/or_lookups/tests.py | 4 +++- tests/prefetch_related/tests.py | 3 ++- 17 files changed, 86 insertions(+), 44 deletions(-) diff --git a/django/db/backends/base/operations.py b/django/db/backends/base/operations.py index 42b66660080e..43b183431e92 100644 --- a/django/db/backends/base/operations.py +++ b/django/db/backends/base/operations.py @@ -897,3 +897,14 @@ def get_hardcoded_pk(self, value): an integer pk can be converted to bson.ObjectId(). """ return value + + def get_nonexistent_pk(self, value): + """ + Given the last created pk (an integer for the built-in backends which + use DEFAULT_AUTO_FIELD=BigAutoField), return a nonexistent primary key + value for use in tests. + + Example use case: MongoDB uses DEFAULT_AUTO_FIELD=ObjectIdAutoField and + can use ObjectId() to generate a nonexistent ID. + """ + return value + 1 diff --git a/tests/admin_changelist/tests.py b/tests/admin_changelist/tests.py index ed1e0f215fdd..491d99fb51c0 100644 --- a/tests/admin_changelist/tests.py +++ b/tests/admin_changelist/tests.py @@ -778,7 +778,8 @@ def test_pk_in_search_fields(self): cl = m.get_changelist_instance(request) self.assertEqual(cl.queryset.count(), 1) - request = self.factory.get("/concert/", data={SEARCH_VAR: band.pk + 5}) + nonexistent_pk = connection.ops.get_nonexistent_pk(band.pk) + request = self.factory.get("/concert/", data={SEARCH_VAR: nonexistent_pk}) request.user = self.superuser cl = m.get_changelist_instance(request) self.assertEqual(cl.queryset.count(), 0) diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py index fe73c049438c..6e32a17ab41a 100644 --- a/tests/admin_views/admin.py +++ b/tests/admin_views/admin.py @@ -12,7 +12,7 @@ from django.contrib.auth.models import Group, User from django.core.exceptions import ValidationError from django.core.mail import EmailMessage -from django.db import models +from django.db import connection, models from django.forms.models import BaseModelFormSet from django.http import HttpResponse, JsonResponse, StreamingHttpResponse from django.urls import path @@ -722,7 +722,8 @@ class FieldOverridePostAdmin(PostAdmin): class CustomChangeList(ChangeList): def get_queryset(self, request): - return self.root_queryset.order_by("pk").filter(pk=9999) # Doesn't exist + nonexistent_pk = connection.ops.get_nonexistent_pk(9999) + return self.root_queryset.order_by("pk").filter(pk=nonexistent_pk) class GadgetAdmin(admin.ModelAdmin): diff --git a/tests/admin_views/test_actions.py b/tests/admin_views/test_actions.py index d82c8f3f5fc1..a6f55650e630 100644 --- a/tests/admin_views/test_actions.py +++ b/tests/admin_views/test_actions.py @@ -103,9 +103,10 @@ def test_model_admin_default_delete_action(self): self.assertEqual(Subscriber.objects.count(), 0) def test_default_delete_action_nonexistent_pk(self): - self.assertFalse(Subscriber.objects.filter(id=9998).exists()) + nonexistent_pk = connection.ops.get_nonexistent_pk(9998) + self.assertFalse(Subscriber.objects.filter(id=nonexistent_pk).exists()) action_data = { - ACTION_CHECKBOX_NAME: ["9998"], + ACTION_CHECKBOX_NAME: [str(nonexistent_pk)], "action": "delete_selected", "index": 0, } diff --git a/tests/aggregation_regress/tests.py b/tests/aggregation_regress/tests.py index ba1d8e77b231..6f652a2194f7 100644 --- a/tests/aggregation_regress/tests.py +++ b/tests/aggregation_regress/tests.py @@ -1475,7 +1475,8 @@ def test_annotate_joins(self): qs = Book.objects.annotate(n=Count("pk")) self.assertIs(qs.query.alias_map["aggregation_regress_book"].join_type, None) # The query executes without problems. - self.assertEqual(len(qs.exclude(publisher=-1)), 6) + id_ = connection.ops.get_nonexistent_pk(-1) + self.assertEqual(len(qs.exclude(publisher=id_)), 6) @skipUnlessDBFeature("allows_group_by_selected_pks") def test_aggregate_duplicate_columns(self): diff --git a/tests/async/test_async_queryset.py b/tests/async/test_async_queryset.py index 374b4576f98f..cc9223f0fc97 100644 --- a/tests/async/test_async_queryset.py +++ b/tests/async/test_async_queryset.py @@ -208,7 +208,7 @@ async def test_acontains(self): self.assertIs(check, True) # Unsaved instances are not allowed, so use an ID known not to exist. check = await SimpleModel.objects.acontains( - SimpleModel(id=self.s3.id + 1, field=4) + SimpleModel(id=connection.ops.get_nonexistent_pk(self.s3.id), field=4) ) self.assertIs(check, False) diff --git a/tests/basic/tests.py b/tests/basic/tests.py index 034a7548da69..e24c58b65b80 100644 --- a/tests/basic/tests.py +++ b/tests/basic/tests.py @@ -670,7 +670,7 @@ def test_does_not_exist(self): ObjectDoesNotExist, "Article matching query does not exist." ): Article.objects.get( - id__exact=2000, + id__exact=connection.ops.get_nonexistent_pk(2000), ) # To avoid dict-ordering related errors check only one lookup # in single assert. diff --git a/tests/forms_tests/tests/test_error_messages.py b/tests/forms_tests/tests/test_error_messages.py index e6bfa35dfb90..fe89f24cc7d8 100644 --- a/tests/forms_tests/tests/test_error_messages.py +++ b/tests/forms_tests/tests/test_error_messages.py @@ -1,5 +1,6 @@ from django.core.exceptions import ValidationError from django.core.files.uploadedfile import SimpleUploadedFile +from django.db import connection from django.forms import ( BooleanField, CharField, @@ -310,9 +311,10 @@ class SomeForm(Form): class ModelChoiceFieldErrorMessagesTestCase(TestCase, AssertFormErrorsMixin): def test_modelchoicefield(self): # Create choices for the model choice field tests below. - ChoiceModel.objects.create(pk=1, name="a") - ChoiceModel.objects.create(pk=2, name="b") - ChoiceModel.objects.create(pk=3, name="c") + ChoiceModel.objects.create(name="a") + ChoiceModel.objects.create(name="b") + choice3 = ChoiceModel.objects.create(name="c") + nonexistent_pk = connection.ops.get_nonexistent_pk(choice3.pk) # ModelChoiceField e = { @@ -321,7 +323,7 @@ def test_modelchoicefield(self): } f = ModelChoiceField(queryset=ChoiceModel.objects.all(), error_messages=e) self.assertFormErrors(["REQUIRED"], f.clean, "") - self.assertFormErrors(["INVALID CHOICE"], f.clean, "4") + self.assertFormErrors(["INVALID CHOICE"], f.clean, str(nonexistent_pk)) # ModelMultipleChoiceField e = { @@ -333,8 +335,12 @@ def test_modelchoicefield(self): queryset=ChoiceModel.objects.all(), error_messages=e ) self.assertFormErrors(["REQUIRED"], f.clean, "") - self.assertFormErrors(["NOT A LIST OF VALUES"], f.clean, "3") - self.assertFormErrors(["4 IS INVALID CHOICE"], f.clean, ["4"]) + self.assertFormErrors(["NOT A LIST OF VALUES"], f.clean, str(choice3.pk)) + self.assertFormErrors( + [f"{nonexistent_pk} IS INVALID CHOICE"], + f.clean, + [str(nonexistent_pk)], + ) def test_modelchoicefield_value_placeholder(self): f = ModelChoiceField( diff --git a/tests/lookup/tests.py b/tests/lookup/tests.py index b154541e788b..1aa6aa00643f 100644 --- a/tests/lookup/tests.py +++ b/tests/lookup/tests.py @@ -198,7 +198,9 @@ def test_in_bulk(self): Article.objects.in_bulk(frozenset([self.a3.id])), {self.a3.id: self.a3} ) self.assertEqual(Article.objects.in_bulk((self.a3.id,)), {self.a3.id: self.a3}) - self.assertEqual(Article.objects.in_bulk([1000]), {}) + self.assertEqual( + Article.objects.in_bulk([connection.ops.get_nonexistent_pk(1000)]), {} + ) self.assertEqual(Article.objects.in_bulk([]), {}) self.assertEqual( Article.objects.in_bulk(iter([self.a1.id])), {self.a1.id: self.a1} diff --git a/tests/model_forms/tests.py b/tests/model_forms/tests.py index 466b7dc57b24..75cb27bd7ef1 100644 --- a/tests/model_forms/tests.py +++ b/tests/model_forms/tests.py @@ -2173,19 +2173,17 @@ def test_model_multiple_choice_field(self): # Add a Category object *after* the ModelMultipleChoiceField has # already been instantiated. This proves clean() checks the database - # during clean() rather than caching it at time of instantiation. Note, - # we are using an id of 1006 here since tests that run before this may - # create categories with primary keys up to 6. Use a number that will - # not conflict. - c6 = Category.objects.create(id=1006, name="Sixth", url="6th") - self.assertCountEqual(f.clean([c6.id]), [c6]) + # during clean() rather than caching it at time of instantiation. + pk = connection.ops.get_nonexistent_pk(self.c3.pk) + c4 = Category.objects.create(id=pk, name="Fourth", url="4th") + self.assertCountEqual(f.clean([c4.id]), [c4]) # Delete a Category object *after* the ModelMultipleChoiceField has # already been instantiated. This proves clean() checks the database # during clean() rather than caching it at time of instantiation. - Category.objects.get(url="6th").delete() + Category.objects.get(url="4th").delete() with self.assertRaises(ValidationError): - f.clean([c6.id]) + f.clean([c4.id]) def test_model_multiple_choice_required_false(self): f = forms.ModelMultipleChoiceField(Category.objects.all(), required=False) diff --git a/tests/model_formsets/tests.py b/tests/model_formsets/tests.py index 57f22cc52cfd..376d821a3fea 100644 --- a/tests/model_formsets/tests.py +++ b/tests/model_formsets/tests.py @@ -5,7 +5,7 @@ from django import forms from django.core.exceptions import ImproperlyConfigured -from django.db import models +from django.db import connection, models from django.forms.formsets import formset_factory from django.forms.models import ( BaseModelFormSet, @@ -136,6 +136,7 @@ def test_outdated_deletion(self): PoemFormSet = inlineformset_factory( Poet, Poem, fields="__all__", can_delete=True ) + nonexistent_pk = connection.ops.get_nonexistent_pk(poem.pk) # Simulate deletion of an object that doesn't exist in the database data = { @@ -143,13 +144,13 @@ def test_outdated_deletion(self): "form-INITIAL_FORMS": "2", "form-0-id": str(poem.pk), "form-0-name": "foo", - "form-1-id": str(poem.pk + 1), # doesn't exist + "form-1-id": str(nonexistent_pk), "form-1-name": "bar", "form-1-DELETE": "on", } formset = PoemFormSet(data, instance=poet, prefix="form") - # The formset is valid even though poem.pk + 1 doesn't exist, + # The formset is valid even though the id in form 1 doesn't exist, # because it's marked for deletion anyway self.assertTrue(formset.is_valid()) @@ -158,7 +159,7 @@ def test_outdated_deletion(self): # Make sure the save went through correctly self.assertEqual(Poem.objects.get(pk=poem.pk).name, "foo") self.assertEqual(poet.poem_set.count(), 1) - self.assertFalse(Poem.objects.filter(pk=poem.pk + 1).exists()) + self.assertFalse(Poem.objects.filter(pk=nonexistent_pk).exists()) class ModelFormsetTest(TestCase): diff --git a/tests/model_formsets_regress/tests.py b/tests/model_formsets_regress/tests.py index 794244b6e8f0..8b9795c81bc2 100644 --- a/tests/model_formsets_regress/tests.py +++ b/tests/model_formsets_regress/tests.py @@ -1,4 +1,5 @@ from django import forms +from django.db import connection from django.forms.formsets import DELETION_FIELD_NAME, BaseFormSet from django.forms.models import ( BaseModelFormSet, @@ -201,15 +202,16 @@ def test_inline_model_with_to_field_to_rel(self): """ FormSet = inlineformset_factory(UserProfile, ProfileNetwork, exclude=[]) - user = User.objects.create(username="guido", serial=1337, pk=1) - self.assertEqual(user.pk, 1) - profile = UserProfile.objects.create(user=user, about="about", pk=2) - self.assertEqual(profile.pk, 2) + user = User.objects.create(username="guido", serial=1337) + profile = UserProfile.objects.create( + user=user, about="about", pk=connection.ops.get_nonexistent_pk(user.pk) + ) + self.assertNotEqual(user.pk, profile.pk) ProfileNetwork.objects.create(profile=profile, network=10, identifier=10) formset = FormSet(instance=profile) # Testing the inline model's relation - self.assertEqual(formset[0].instance.profile_id, 1) + self.assertEqual(formset[0].instance.profile_id, user.pk) def test_formset_with_none_instance(self): "A formset with instance=None can be created. Regression for #11872" diff --git a/tests/model_inheritance/tests.py b/tests/model_inheritance/tests.py index 4b8db72e5db0..0205ba359702 100644 --- a/tests/model_inheritance/tests.py +++ b/tests/model_inheritance/tests.py @@ -451,7 +451,7 @@ def test_inherited_multiple_objects_returned_exception(self): def test_inherited_not_updated_exception(self): # NotUpdated is also inherited. - obj = Restaurant(id=999) + obj = Restaurant(id=connection.ops.get_nonexistent_pk(999)) with self.assertRaises(Place.NotUpdated): obj.save(update_fields={"name"}) diff --git a/tests/model_inheritance_regress/tests.py b/tests/model_inheritance_regress/tests.py index ab4edea0acc7..7c2d0b1eadbc 100644 --- a/tests/model_inheritance_regress/tests.py +++ b/tests/model_inheritance_regress/tests.py @@ -7,6 +7,7 @@ from unittest import expectedFailure from django import forms +from django.db import connection from django.db.models import FETCH_PEERS from django.test import TestCase from django.test.utils import ignore_warnings @@ -439,11 +440,16 @@ def test_abstract_verbose_name_plural_inheritance(self): def test_inherited_nullable_exclude(self): obj = SelfRefChild.objects.create(child_data=37, parent_data=42) + nonexistent_pk = connection.ops.get_nonexistent_pk(72) self.assertQuerySetEqual( - SelfRefParent.objects.exclude(self_data=72), [obj.pk], attrgetter("pk") + SelfRefParent.objects.exclude(self_data=nonexistent_pk), + [obj.pk], + attrgetter("pk"), ) self.assertQuerySetEqual( - SelfRefChild.objects.exclude(self_data=72), [obj.pk], attrgetter("pk") + SelfRefChild.objects.exclude(self_data=nonexistent_pk), + [obj.pk], + attrgetter("pk"), ) def test_concrete_abstract_concrete_pk(self): diff --git a/tests/multiple_database/tests.py b/tests/multiple_database/tests.py index 42b2ba2486a1..f0cc3b668de9 100644 --- a/tests/multiple_database/tests.py +++ b/tests/multiple_database/tests.py @@ -7,7 +7,7 @@ from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.core import management -from django.db import DEFAULT_DB_ALIAS, router, transaction +from django.db import DEFAULT_DB_ALIAS, connection, router, transaction from django.db.models import signals from django.db.utils import ConnectionRouter from django.test import SimpleTestCase, TestCase, override_settings @@ -1668,16 +1668,21 @@ def test_m2m_cross_database_protection(self): "M2M relations can cross databases if the database share a source" # Create books and authors on the inverse to the usual database pro = Book.objects.using("other").create( - pk=1, title="Pro Django", published=datetime.date(2008, 12, 16) + title="Pro Django", published=datetime.date(2008, 12, 16) ) - marty = Person.objects.using("other").create(pk=1, name="Marty Alchin") + marty = Person.objects.using("other").create(name="Marty Alchin") dive = Book.objects.using("default").create( - pk=2, title="Dive into Python", published=datetime.date(2009, 5, 4) + pk=connection.ops.get_nonexistent_pk(pro.pk), + title="Dive into Python", + published=datetime.date(2009, 5, 4), ) - mark = Person.objects.using("default").create(pk=2, name="Mark Pilgrim") + mark = Person.objects.using("default").create( + pk=connection.ops.get_nonexistent_pk(marty.pk), + name="Mark Pilgrim", + ) # Now save back onto the usual database. # This simulates primary/replica - the objects exist on both database, @@ -1760,7 +1765,9 @@ def test_m2m_cross_database_protection(self): # If you create an object through a M2M relation, it will be # written to the write database, even if the original object # was on the read database - alice = dive.authors.create(name="Alice", pk=3) + alice = dive.authors.create( + name="Alice", pk=connection.ops.get_nonexistent_pk(mark.pk) + ) self.assertEqual(alice._state.db, "default") # Same goes for get_or_create, regardless of whether getting or @@ -1768,7 +1775,9 @@ def test_m2m_cross_database_protection(self): alice, created = dive.authors.get_or_create(name="Alice") self.assertEqual(alice._state.db, "default") - bob, created = dive.authors.get_or_create(name="Bob", defaults={"pk": 4}) + bob, created = dive.authors.get_or_create( + name="Bob", defaults={"pk": connection.ops.get_nonexistent_pk(alice.pk)} + ) self.assertEqual(bob._state.db, "default") def test_o2o_cross_database_protection(self): diff --git a/tests/or_lookups/tests.py b/tests/or_lookups/tests.py index bfcb32bea75e..21ebf1c6110b 100644 --- a/tests/or_lookups/tests.py +++ b/tests/or_lookups/tests.py @@ -1,6 +1,7 @@ from datetime import datetime from operator import attrgetter +from django.db import connection from django.db.models import Q from django.test import TestCase @@ -94,8 +95,9 @@ def test_pk_in(self): attrgetter("headline"), ) + nonexistent_pk = connection.ops.get_nonexistent_pk(40000) self.assertQuerySetEqual( - Article.objects.filter(pk__in=[self.a1, self.a2, self.a3, 40000]), + Article.objects.filter(pk__in=[self.a1, self.a2, self.a3, nonexistent_pk]), ["Hello", "Goodbye", "Hello and goodbye"], attrgetter("headline"), ) diff --git a/tests/prefetch_related/tests.py b/tests/prefetch_related/tests.py index cc3034b63333..a7e92113bf2a 100644 --- a/tests/prefetch_related/tests.py +++ b/tests/prefetch_related/tests.py @@ -177,11 +177,12 @@ def test_onetoone_reverse_with_to_field_pk(self): self.assertEqual(author.name, author.bio.author.name) def test_survives_clone(self): + nonexistent_pk = connection.ops.get_nonexistent_pk(1000) with self.assertNumQueries(2): [ list(b.first_time_authors.all()) for b in Book.objects.prefetch_related("first_time_authors").exclude( - id=1000 + id=nonexistent_pk ) ]