From 0aff3fd7110d1bd431a35bb28d8688693b98d886 Mon Sep 17 00:00:00 2001
From: Timothy McCurrach <tim.mccurrach@gmail.com>
Date: Fri, 15 Jan 2021 14:32:54 +0000
Subject: [PATCH] Fixed #18549 -- Fixed heading for inlines with a
 OneToOneField.

Used verbose_name instead of verbose_name_plural.
---
 .../admin/templates/admin/edit_inline/stacked.html  |  6 +++++-
 .../admin/templates/admin/edit_inline/tabular.html  |  6 +++++-
 tests/admin_inlines/admin.py                        | 13 +++++++++++--
 tests/admin_inlines/models.py                       |  1 +
 tests/admin_inlines/tests.py                        |  5 +++++
 5 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/django/contrib/admin/templates/admin/edit_inline/stacked.html b/django/contrib/admin/templates/admin/edit_inline/stacked.html
index 0292c6ef99..25b7b58e4e 100644
--- a/django/contrib/admin/templates/admin/edit_inline/stacked.html
+++ b/django/contrib/admin/templates/admin/edit_inline/stacked.html
@@ -4,7 +4,11 @@
      data-inline-type="stacked"
      data-inline-formset="{{ inline_admin_formset.inline_formset_data }}">
 <fieldset class="module {{ inline_admin_formset.classes }}">
-  <h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}</h2>
+  {% if inline_admin_formset.formset.max_num == 1 %}
+    <h2>{{ inline_admin_formset.opts.verbose_name|capfirst }}</h2>
+  {% else %}
+    <h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}</h2>
+  {% endif %}
 {{ inline_admin_formset.formset.management_form }}
 {{ inline_admin_formset.formset.non_form_errors }}
 
diff --git a/django/contrib/admin/templates/admin/edit_inline/tabular.html b/django/contrib/admin/templates/admin/edit_inline/tabular.html
index c19d284062..7a4e7cb226 100644
--- a/django/contrib/admin/templates/admin/edit_inline/tabular.html
+++ b/django/contrib/admin/templates/admin/edit_inline/tabular.html
@@ -5,7 +5,11 @@
   <div class="tabular inline-related {% if forloop.last %}last-related{% endif %}">
 {{ inline_admin_formset.formset.management_form }}
 <fieldset class="module {{ inline_admin_formset.classes }}">
-   <h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}</h2>
+   {% if inline_admin_formset.formset.max_num == 1 %}
+     <h2>{{ inline_admin_formset.opts.verbose_name|capfirst }}</h2>
+   {% else %}
+     <h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}</h2>
+   {% endif %}
    {{ inline_admin_formset.formset.non_form_errors }}
    <table>
      <thead><tr>
diff --git a/tests/admin_inlines/admin.py b/tests/admin_inlines/admin.py
index 5c968870e4..50266308e1 100644
--- a/tests/admin_inlines/admin.py
+++ b/tests/admin_inlines/admin.py
@@ -9,8 +9,8 @@ from .models import (
     Holder, Holder2, Holder3, Holder4, Holder5, Inner, Inner2, Inner3,
     Inner4Stacked, Inner4Tabular, Inner5Stacked, Inner5Tabular, NonAutoPKBook,
     NonAutoPKBookChild, Novel, NovelReadonlyChapter, OutfitItem,
-    ParentModelWithCustomPk, Poll, Profile, ProfileCollection, Question,
-    ReadOnlyInline, ShoppingWeakness, Sighting, SomeChildModel,
+    ParentModelWithCustomPk, Person, Poll, Profile, ProfileCollection,
+    Question, ReadOnlyInline, ShoppingWeakness, Sighting, SomeChildModel,
     SomeParentModel, SottoCapo, Teacher, Title, TitleCollection,
 )
 
@@ -292,6 +292,14 @@ class TeacherAdmin(admin.ModelAdmin):
     inlines = [StudentInline]
 
 
+class AuthorTabularInline(admin.TabularInline):
+    model = Author
+
+
+class FashonistaStackedInline(admin.StackedInline):
+    model = Fashionista
+
+
 site.register(TitleCollection, inlines=[TitleInline])
 # Test bug #12561 and #12778
 # only ModelAdmin media
@@ -318,3 +326,4 @@ site.register([Question, Inner4Stacked, Inner4Tabular])
 site.register(Teacher, TeacherAdmin)
 site.register(Chapter, inlines=[FootNoteNonEditableInlineCustomForm])
 site.register(OutfitItem, inlines=[WeaknessInlineCustomForm])
+site.register(Person, inlines=[AuthorTabularInline, FashonistaStackedInline])
diff --git a/tests/admin_inlines/models.py b/tests/admin_inlines/models.py
index 501020f188..241eceebb2 100644
--- a/tests/admin_inlines/models.py
+++ b/tests/admin_inlines/models.py
@@ -44,6 +44,7 @@ class Book(models.Model):
 class Author(models.Model):
     name = models.CharField(max_length=50)
     books = models.ManyToManyField(Book)
+    person = models.OneToOneField('Person', models.CASCADE, null=True)
 
 
 class NonAutoPKBook(models.Model):
diff --git a/tests/admin_inlines/tests.py b/tests/admin_inlines/tests.py
index c71bf1efed..7af6972585 100644
--- a/tests/admin_inlines/tests.py
+++ b/tests/admin_inlines/tests.py
@@ -485,6 +485,11 @@ class TestInline(TestDataMixin, TestCase):
         self.assertContains(response, '<h2>Inner4 stackeds</h2>', html=True)
         self.assertContains(response, '<h2>Inner4 tabulars</h2>', html=True)
 
+    def test_inlines_singular_heading_one_to_one(self):
+        response = self.client.get(reverse('admin:admin_inlines_person_add'))
+        self.assertContains(response, '<h2>Author</h2>', html=True)  # Tabular.
+        self.assertContains(response, '<h2>Fashionista</h2>', html=True)  # Stacked.
+
 
 @override_settings(ROOT_URLCONF='admin_inlines.urls')
 class TestInlineMedia(TestDataMixin, TestCase):