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
)
]