Fixed #36498 -- Changed inline admin button text to use "Add" instead of "Add another"

This commit is contained in:
Dani Fornons 2025-11-12 12:33:06 +01:00
parent a1ce852e52
commit 8a82e4a177
2 changed files with 48 additions and 54 deletions

View file

@ -427,7 +427,7 @@ class InlineAdminFormSet:
"name": "#%s" % self.formset.prefix, "name": "#%s" % self.formset.prefix,
"options": { "options": {
"prefix": self.formset.prefix, "prefix": self.formset.prefix,
"addText": gettext("Add another %(verbose_name)s") "addText": gettext("Add %(verbose_name)s")
% { % {
"verbose_name": capfirst(verbose_name), "verbose_name": capfirst(verbose_name),
}, },

View file

@ -143,8 +143,8 @@ class TestInline(TestDataMixin, TestCase):
), ),
html=True, html=True,
) )
# The "add another" label is correct # The "add" label is correct
self.assertContains(response, "Add another Author-book relationship") self.assertContains(response, "Add Author-book relationship")
# The '+' is dropped from the autogenerated form prefix (Author_books+) # The '+' is dropped from the autogenerated form prefix (Author_books+)
self.assertContains(response, 'id="id_Author_books-TOTAL_FORMS"') self.assertContains(response, 'id="id_Author_books-TOTAL_FORMS"')
@ -990,7 +990,7 @@ class TestInlinePermissions(TestCase):
), ),
html=True, html=True,
) )
self.assertNotContains(response, "Add another Author-Book Relationship") self.assertNotContains(response, "Add Author-Book Relationship")
self.assertNotContains(response, 'id="id_Author_books-TOTAL_FORMS"') self.assertNotContains(response, 'id="id_Author_books-TOTAL_FORMS"')
def test_inline_add_fk_noperm(self): def test_inline_add_fk_noperm(self):
@ -1001,7 +1001,7 @@ class TestInlinePermissions(TestCase):
'<h2 id="inner2_set-2-heading" class="inline-heading">Inner2s</h2>', '<h2 id="inner2_set-2-heading" class="inline-heading">Inner2s</h2>',
html=True, html=True,
) )
self.assertNotContains(response, "Add another Inner2") self.assertNotContains(response, "Add Inner2")
self.assertNotContains(response, 'id="id_inner2_set-TOTAL_FORMS"') self.assertNotContains(response, 'id="id_inner2_set-TOTAL_FORMS"')
def test_inline_change_m2m_noperm(self): def test_inline_change_m2m_noperm(self):
@ -1015,7 +1015,7 @@ class TestInlinePermissions(TestCase):
), ),
html=True, html=True,
) )
self.assertNotContains(response, "Add another Author-Book Relationship") self.assertNotContains(response, "Add Author-Book Relationship")
self.assertNotContains(response, 'id="id_Author_books-TOTAL_FORMS"') self.assertNotContains(response, 'id="id_Author_books-TOTAL_FORMS"')
def test_inline_change_fk_noperm(self): def test_inline_change_fk_noperm(self):
@ -1026,7 +1026,7 @@ class TestInlinePermissions(TestCase):
'<h2 id="inner2_set-2-heading" class="inline-heading">Inner2s</h2>', '<h2 id="inner2_set-2-heading" class="inline-heading">Inner2s</h2>',
html=True, html=True,
) )
self.assertNotContains(response, "Add another Inner2") self.assertNotContains(response, "Add Inner2")
self.assertNotContains(response, 'id="id_inner2_set-TOTAL_FORMS"') self.assertNotContains(response, 'id="id_inner2_set-TOTAL_FORMS"')
def test_inline_add_m2m_view_only_perm(self): def test_inline_add_m2m_view_only_perm(self):
@ -1063,7 +1063,7 @@ class TestInlinePermissions(TestCase):
'id="id_Author_books-TOTAL_FORMS">', 'id="id_Author_books-TOTAL_FORMS">',
html=True, html=True,
) )
self.assertNotContains(response, "Add another Author-Book Relationship") self.assertNotContains(response, "Add Author-Book Relationship")
def test_inline_add_m2m_add_perm(self): def test_inline_add_m2m_add_perm(self):
permission = Permission.objects.get( permission = Permission.objects.get(
@ -1080,7 +1080,7 @@ class TestInlinePermissions(TestCase):
), ),
html=True, html=True,
) )
self.assertNotContains(response, "Add another Author-Book Relationship") self.assertNotContains(response, "Add Author-Book Relationship")
self.assertNotContains(response, 'id="id_Author_books-TOTAL_FORMS"') self.assertNotContains(response, 'id="id_Author_books-TOTAL_FORMS"')
def test_inline_add_fk_add_perm(self): def test_inline_add_fk_add_perm(self):
@ -1095,7 +1095,7 @@ class TestInlinePermissions(TestCase):
'<h2 id="inner2_set-2-heading" class="inline-heading">Inner2s</h2>', '<h2 id="inner2_set-2-heading" class="inline-heading">Inner2s</h2>',
html=True, html=True,
) )
self.assertContains(response, "Add another Inner2") self.assertContains(response, "Add Inner2")
self.assertContains( self.assertContains(
response, response,
'<input type="hidden" id="id_inner2_set-TOTAL_FORMS" ' '<input type="hidden" id="id_inner2_set-TOTAL_FORMS" '
@ -1118,7 +1118,7 @@ class TestInlinePermissions(TestCase):
), ),
html=True, html=True,
) )
self.assertNotContains(response, "Add another Author-Book Relationship") self.assertNotContains(response, "Add Author-Book Relationship")
self.assertNotContains(response, 'id="id_Author_books-TOTAL_FORMS"') self.assertNotContains(response, 'id="id_Author_books-TOTAL_FORMS"')
self.assertNotContains(response, 'id="id_Author_books-0-DELETE"') self.assertNotContains(response, 'id="id_Author_books-0-DELETE"')
@ -1189,7 +1189,7 @@ class TestInlinePermissions(TestCase):
), ),
html=True, html=True,
) )
self.assertContains(response, "Add another Author-book relationship") self.assertContains(response, "Add Author-book relationship")
self.assertContains( self.assertContains(
response, response,
'<input type="hidden" id="id_Author_books-TOTAL_FORMS" ' '<input type="hidden" id="id_Author_books-TOTAL_FORMS" '
@ -1216,7 +1216,7 @@ class TestInlinePermissions(TestCase):
'<h2 id="inner2_set-2-heading" class="inline-heading">Inner2s</h2>', '<h2 id="inner2_set-2-heading" class="inline-heading">Inner2s</h2>',
html=True, html=True,
) )
self.assertContains(response, "Add another Inner2") self.assertContains(response, "Add Inner2")
# 3 extra forms only, not the existing instance form # 3 extra forms only, not the existing instance form
self.assertContains( self.assertContains(
response, response,
@ -1526,7 +1526,7 @@ class TestVerboseNameInlineForms(TestDataMixin, TestCase):
request = self.factory.get(url) request = self.factory.get(url)
request.user = self.superuser request.user = self.superuser
response = modeladmin.changeform_view(request) response = modeladmin.changeform_view(request)
self.assertNotContains(response, "Add another Profile") self.assertNotContains(response, "Add Profile")
# Non-verbose model. # Non-verbose model.
self.assertContains( self.assertContains(
response, response,
@ -1536,7 +1536,7 @@ class TestVerboseNameInlineForms(TestDataMixin, TestCase):
), ),
html=True, html=True,
) )
self.assertContains(response, "Add another Non-verbose child") self.assertContains(response, "Add Non-verbose child")
self.assertNotContains( self.assertNotContains(
response, response,
'<h2 id="profile_set-heading" class="inline-heading">Profiles</h2>', '<h2 id="profile_set-heading" class="inline-heading">Profiles</h2>',
@ -1551,14 +1551,14 @@ class TestVerboseNameInlineForms(TestDataMixin, TestCase):
), ),
html=True, html=True,
) )
self.assertContains(response, "Add another Childs with verbose name") self.assertContains(response, "Add Childs with verbose name")
self.assertNotContains( self.assertNotContains(
response, response,
'<h2 id="verbosenameprofile_set-heading" class="inline-heading">' '<h2 id="verbosenameprofile_set-heading" class="inline-heading">'
"Model with verbose name onlys</h2>", "Model with verbose name onlys</h2>",
html=True, html=True,
) )
self.assertNotContains(response, "Add another Model with verbose name only") self.assertNotContains(response, "Add Model with verbose name only")
# Model with verbose name plural. # Model with verbose name plural.
self.assertContains( self.assertContains(
response, response,
@ -1568,7 +1568,7 @@ class TestVerboseNameInlineForms(TestDataMixin, TestCase):
), ),
html=True, html=True,
) )
self.assertContains(response, "Add another Childs with verbose name plural") self.assertContains(response, "Add Childs with verbose name plural")
self.assertNotContains( self.assertNotContains(
response, response,
'<h2 id="verbosenamepluralprofile_set-heading" class="inline-heading">' '<h2 id="verbosenamepluralprofile_set-heading" class="inline-heading">'
@ -1584,14 +1584,14 @@ class TestVerboseNameInlineForms(TestDataMixin, TestCase):
), ),
html=True, html=True,
) )
self.assertContains(response, "Add another Childs with both verbose names") self.assertContains(response, "Add Childs with both verbose names")
self.assertNotContains( self.assertNotContains(
response, response,
'<h2 id="bothverbosenameprofile_set-heading" class="inline-heading">' '<h2 id="bothverbosenameprofile_set-heading" class="inline-heading">'
"Model with both - plural name</h2>", "Model with both - plural name</h2>",
html=True, html=True,
) )
self.assertNotContains(response, "Add another Model with both - name") self.assertNotContains(response, "Add Model with both - name")
def test_verbose_name_plural_inline(self): def test_verbose_name_plural_inline(self):
class NonVerboseProfileInline(TabularInline): class NonVerboseProfileInline(TabularInline):
@ -1631,7 +1631,7 @@ class TestVerboseNameInlineForms(TestDataMixin, TestCase):
), ),
html=True, html=True,
) )
self.assertContains(response, "Add another Profile") self.assertContains(response, "Add Profile")
self.assertNotContains( self.assertNotContains(
response, response,
'<h2 id="profile_set-heading" class="inline-heading">Profiles</h2>', '<h2 id="profile_set-heading" class="inline-heading">Profiles</h2>',
@ -1646,7 +1646,7 @@ class TestVerboseNameInlineForms(TestDataMixin, TestCase):
), ),
html=True, html=True,
) )
self.assertContains(response, "Add another Model with verbose name only") self.assertContains(response, "Add Model with verbose name only")
self.assertNotContains( self.assertNotContains(
response, response,
'<h2 id="verbosenameprofile_set-heading" class="inline-heading">' '<h2 id="verbosenameprofile_set-heading" class="inline-heading">'
@ -1662,7 +1662,7 @@ class TestVerboseNameInlineForms(TestDataMixin, TestCase):
), ),
html=True, html=True,
) )
self.assertContains(response, "Add another Profile") self.assertContains(response, "Add Profile")
self.assertNotContains( self.assertNotContains(
response, response,
'<h2 id="verbosenamepluralprofile_set-heading" class="inline-heading">' '<h2 id="verbosenamepluralprofile_set-heading" class="inline-heading">'
@ -1678,7 +1678,7 @@ class TestVerboseNameInlineForms(TestDataMixin, TestCase):
), ),
html=True, html=True,
) )
self.assertContains(response, "Add another Model with both - name") self.assertContains(response, "Add Model with both - name")
self.assertNotContains( self.assertNotContains(
response, response,
'<h2 id="bothverbosenameprofile_set-heading" class="inline-heading">' '<h2 id="bothverbosenameprofile_set-heading" class="inline-heading">'
@ -1719,7 +1719,7 @@ class TestVerboseNameInlineForms(TestDataMixin, TestCase):
request = self.factory.get(url) request = self.factory.get(url)
request.user = self.superuser request.user = self.superuser
response = modeladmin.changeform_view(request) response = modeladmin.changeform_view(request)
self.assertNotContains(response, "Add another Profile") self.assertNotContains(response, "Add Profile")
# Non-verbose model. # Non-verbose model.
self.assertContains( self.assertContains(
response, response,
@ -1729,7 +1729,7 @@ class TestVerboseNameInlineForms(TestDataMixin, TestCase):
), ),
html=True, html=True,
) )
self.assertContains(response, "Add another Non-verbose childs - name") self.assertContains(response, "Add Non-verbose childs - name")
self.assertNotContains( self.assertNotContains(
response, response,
'<h2 id="profile_set-heading" class="inline-heading">Profiles</h2>', '<h2 id="profile_set-heading" class="inline-heading">Profiles</h2>',
@ -1744,7 +1744,7 @@ class TestVerboseNameInlineForms(TestDataMixin, TestCase):
), ),
html=True, html=True,
) )
self.assertContains(response, "Add another Childs with verbose name - name") self.assertContains(response, "Add Childs with verbose name - name")
self.assertNotContains( self.assertNotContains(
response, response,
'<h2 id="verbosenameprofile_set-heading" class="inline-heading">' '<h2 id="verbosenameprofile_set-heading" class="inline-heading">'
@ -1762,7 +1762,7 @@ class TestVerboseNameInlineForms(TestDataMixin, TestCase):
) )
self.assertContains( self.assertContains(
response, response,
"Add another Childs with verbose name plural - name", "Add Childs with verbose name plural - name",
) )
self.assertNotContains( self.assertNotContains(
response, response,
@ -1777,14 +1777,14 @@ class TestVerboseNameInlineForms(TestDataMixin, TestCase):
"Childs with both - plural name</h2>", "Childs with both - plural name</h2>",
html=True, html=True,
) )
self.assertContains(response, "Add another Childs with both - name") self.assertContains(response, "Add Childs with both - name")
self.assertNotContains( self.assertNotContains(
response, response,
'<h2 id="bothverbosenameprofile_set-heading" class="inline-heading">' '<h2 id="bothverbosenameprofile_set-heading" class="inline-heading">'
"Model with both - plural name</h2>", "Model with both - plural name</h2>",
html=True, html=True,
) )
self.assertNotContains(response, "Add another Model with both - name") self.assertNotContains(response, "Add Model with both - name")
@override_settings(ROOT_URLCONF="admin_inlines.urls") @override_settings(ROOT_URLCONF="admin_inlines.urls")
@ -1892,7 +1892,7 @@ class SeleniumTests(AdminSeleniumTestCase):
@screenshot_cases(["desktop_size", "mobile_size", "dark", "high_contrast"]) @screenshot_cases(["desktop_size", "mobile_size", "dark", "high_contrast"])
def test_add_stackeds(self): def test_add_stackeds(self):
""" """
The "Add another XXX" link correctly adds items to the stacked formset. The "Add XXX" link correctly adds items to the stacked formset.
""" """
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
@ -1905,9 +1905,7 @@ class SeleniumTests(AdminSeleniumTestCase):
rows_selector = "%s .dynamic-inner4stacked_set" % inline_id rows_selector = "%s .dynamic-inner4stacked_set" % inline_id
self.assertCountSeleniumElements(rows_selector, 3) self.assertCountSeleniumElements(rows_selector, 3)
add_button = self.selenium.find_element( add_button = self.selenium.find_element(By.LINK_TEXT, "Add Inner4 stacked")
By.LINK_TEXT, "Add another Inner4 stacked"
)
add_button.click() add_button.click()
self.assertCountSeleniumElements(rows_selector, 4) self.assertCountSeleniumElements(rows_selector, 4)
self.take_screenshot("added") self.take_screenshot("added")
@ -1925,9 +1923,7 @@ class SeleniumTests(AdminSeleniumTestCase):
self.assertCountSeleniumElements(rows_selector, 3) self.assertCountSeleniumElements(rows_selector, 3)
add_button = self.selenium.find_element( add_button = self.selenium.find_element(By.LINK_TEXT, "Add Inner4 stacked")
By.LINK_TEXT, "Add another Inner4 stacked"
)
add_button.click() add_button.click()
add_button.click() add_button.click()
@ -1955,7 +1951,7 @@ class SeleniumTests(AdminSeleniumTestCase):
add_button = self.selenium.find_element( add_button = self.selenium.find_element(
By.LINK_TEXT, By.LINK_TEXT,
"Add another Inner4 stacked", "Add Inner4 stacked",
) )
add_button.click() add_button.click()
add_button.click() add_button.click()
@ -2019,9 +2015,7 @@ class SeleniumTests(AdminSeleniumTestCase):
self.assertCountSeleniumElements(rows_selector, 3) self.assertCountSeleniumElements(rows_selector, 3)
add_button = self.selenium.find_element( add_button = self.selenium.find_element(By.LINK_TEXT, "Add Inner4 tabular")
By.LINK_TEXT, "Add another Inner4 tabular"
)
add_button.click() add_button.click()
add_button.click() add_button.click()
self.assertCountSeleniumElements("#id_inner4tabular_set-4-dummy", 1) self.assertCountSeleniumElements("#id_inner4tabular_set-4-dummy", 1)
@ -2076,7 +2070,7 @@ class SeleniumTests(AdminSeleniumTestCase):
def test_add_inlines(self): def test_add_inlines(self):
""" """
The "Add another XXX" link correctly adds items to the inline form. The "Add XXX" link correctly adds items to the inline form.
""" """
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
@ -2101,7 +2095,7 @@ class SeleniumTests(AdminSeleniumTestCase):
) )
# Add an inline # Add an inline
self.selenium.find_element(By.LINK_TEXT, "Add another Profile").click() self.selenium.find_element(By.LINK_TEXT, "Add Profile").click()
# The inline has been added, it has the right id, and it contains the # The inline has been added, it has the right id, and it contains the
# correct fields. # correct fields.
@ -2118,8 +2112,8 @@ class SeleniumTests(AdminSeleniumTestCase):
self.assertCountSeleniumElements( self.assertCountSeleniumElements(
".dynamic-profile_set#profile_set-1 input[name=profile_set-1-last_name]", 1 ".dynamic-profile_set#profile_set-1 input[name=profile_set-1-last_name]", 1
) )
# Let's add another one to be sure # Let's add one to be sure
self.selenium.find_element(By.LINK_TEXT, "Add another Profile").click() self.selenium.find_element(By.LINK_TEXT, "Add Profile").click()
self.assertCountSeleniumElements(".dynamic-profile_set", 3) self.assertCountSeleniumElements(".dynamic-profile_set", 3)
self.assertEqual( self.assertEqual(
self.selenium.find_elements(By.CSS_SELECTOR, ".dynamic-profile_set")[ self.selenium.find_elements(By.CSS_SELECTOR, ".dynamic-profile_set")[
@ -2184,7 +2178,7 @@ class SeleniumTests(AdminSeleniumTestCase):
self.selenium.get(self.live_server_url + change_url) self.selenium.get(self.live_server_url + change_url)
with self.disable_implicit_wait(): with self.disable_implicit_wait():
with self.assertRaises(NoSuchElementException): with self.assertRaises(NoSuchElementException):
self.selenium.find_element(By.LINK_TEXT, "Add another Question") self.selenium.find_element(By.LINK_TEXT, "Add Question")
def test_delete_inlines(self): def test_delete_inlines(self):
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
@ -2195,10 +2189,10 @@ class SeleniumTests(AdminSeleniumTestCase):
) )
# Add a few inlines # Add a few inlines
self.selenium.find_element(By.LINK_TEXT, "Add another Profile").click() self.selenium.find_element(By.LINK_TEXT, "Add Profile").click()
self.selenium.find_element(By.LINK_TEXT, "Add another Profile").click() self.selenium.find_element(By.LINK_TEXT, "Add Profile").click()
self.selenium.find_element(By.LINK_TEXT, "Add another Profile").click() self.selenium.find_element(By.LINK_TEXT, "Add Profile").click()
self.selenium.find_element(By.LINK_TEXT, "Add another Profile").click() self.selenium.find_element(By.LINK_TEXT, "Add Profile").click()
self.assertCountSeleniumElements( self.assertCountSeleniumElements(
"#profile_set-group table tr.dynamic-profile_set", 5 "#profile_set-group table tr.dynamic-profile_set", 5
) )
@ -2278,7 +2272,7 @@ class SeleniumTests(AdminSeleniumTestCase):
self.selenium.get( self.selenium.get(
self.live_server_url + reverse("admin:admin_inlines_teacher_add") self.live_server_url + reverse("admin:admin_inlines_teacher_add")
) )
add_text = gettext("Add another %(verbose_name)s") % {"verbose_name": "Child"} add_text = gettext("Add %(verbose_name)s") % {"verbose_name": "Child"}
self.selenium.find_element(By.LINK_TEXT, add_text).click() self.selenium.find_element(By.LINK_TEXT, add_text).click()
test_fields = ["#id_child_set-0-name", "#id_child_set-1-name"] test_fields = ["#id_child_set-0-name", "#id_child_set-1-name"]
summaries = self.selenium.find_elements(By.TAG_NAME, "summary") summaries = self.selenium.find_elements(By.TAG_NAME, "summary")
@ -2406,7 +2400,7 @@ class SeleniumTests(AdminSeleniumTestCase):
def test_inlines_verbose_name(self): def test_inlines_verbose_name(self):
""" """
The item added by the "Add another XXX" link must use the correct The item added by the "Add XXX" link must use the correct
verbose_name in the inline form. verbose_name in the inline form.
""" """
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
@ -2446,7 +2440,7 @@ class SeleniumTests(AdminSeleniumTestCase):
self.assertIn("Available attendant", available.text) self.assertIn("Available attendant", available.text)
self.assertIn("Chosen attendant", chosen.text) self.assertIn("Chosen attendant", chosen.text)
# Added inline should also have the correct verbose_name. # Added inline should also have the correct verbose_name.
self.selenium.find_element(By.LINK_TEXT, "Add another Class").click() self.selenium.find_element(By.LINK_TEXT, "Add Class").click()
available = self.selenium.find_element( available = self.selenium.find_element(
By.CSS_SELECTOR, css_available_selector % 1 By.CSS_SELECTOR, css_available_selector % 1
) )
@ -2456,7 +2450,7 @@ class SeleniumTests(AdminSeleniumTestCase):
self.assertIn("Available attendant", available.text) self.assertIn("Available attendant", available.text)
self.assertIn("Chosen attendant", chosen.text) self.assertIn("Chosen attendant", chosen.text)
# Third inline should also have the correct verbose_name. # Third inline should also have the correct verbose_name.
self.selenium.find_element(By.LINK_TEXT, "Add another Class").click() self.selenium.find_element(By.LINK_TEXT, "Add Class").click()
available = self.selenium.find_element( available = self.selenium.find_element(
By.CSS_SELECTOR, css_available_selector % 2 By.CSS_SELECTOR, css_available_selector % 2
) )