diff --git a/django/db/models/options.py b/django/db/models/options.py index 65d96e033a..a3697c2a13 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -19,7 +19,7 @@ except NameError: # Calculate the verbose_name by converting from InitialCaps to "lowercase with spaces". get_verbose_name = lambda class_name: re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))', ' \\1', class_name).lower().strip() -DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering', +DEFAULT_NAMES = ('verbose_name', 'verbose_name_plural', 'db_table', 'ordering', 'unique_together', 'permissions', 'get_latest_by', 'order_with_respect_to', 'app_label', 'db_tablespace', 'abstract', 'managed', 'proxy', 'auto_created') @@ -91,7 +91,8 @@ class Options(object): # verbose_name_plural is a special case because it uses a 's' # by default. - self.verbose_name_plural = meta_attrs.pop('verbose_name_plural', string_concat(self.verbose_name, 's')) + if self.verbose_name_plural is None: + self.verbose_name_plural = string_concat(self.verbose_name, 's') # Any leftover attributes must be invalid. if meta_attrs != {}: diff --git a/tests/regressiontests/model_inheritance_regress/models.py b/tests/regressiontests/model_inheritance_regress/models.py index 08a51bdf69..787f163349 100644 --- a/tests/regressiontests/model_inheritance_regress/models.py +++ b/tests/regressiontests/model_inheritance_regress/models.py @@ -103,6 +103,20 @@ class DerivedM(BaseM): return "PK = %d, base_name = %s, derived_name = %s" \ % (self.customPK, self.base_name, self.derived_name) +class AuditBase(models.Model): + planned_date = models.DateField() + + class Meta: + abstract = True + verbose_name_plural = u'Audits' + +class CertificationAudit(AuditBase): + class Meta(AuditBase.Meta): + abstract = True + +class InternalCertificationAudit(CertificationAudit): + auditing_dept = models.CharField(max_length=20) + # Check that abstract classes don't get m2m tables autocreated. class Person(models.Model): name = models.CharField(max_length=100) diff --git a/tests/regressiontests/model_inheritance_regress/tests.py b/tests/regressiontests/model_inheritance_regress/tests.py index 012cd67325..569cef5845 100644 --- a/tests/regressiontests/model_inheritance_regress/tests.py +++ b/tests/regressiontests/model_inheritance_regress/tests.py @@ -8,7 +8,7 @@ from regressiontests.model_inheritance_regress.models import ( Place, Restaurant, ItalianRestaurant, ParkingLot, ParkingLot2, ParkingLot3, Supplier, Wholesaler, Child, SelfRefChild, ArticleWithAuthor, M2MChild, QualityControl, DerivedM, Person, BirthdayParty, BachelorParty, - MessyBachelorParty) + MessyBachelorParty, InternalCertificationAudit) class ModelInheritanceTest(TestCase): def test_model_inheritance(self): @@ -353,3 +353,14 @@ class ModelInheritanceTest(TestCase): parties = list(p4.bachelorparty_set.all()) self.assertEqual(parties, [bachelor, messy_parent]) + + def test_11369(self): + """verbose_name_plural correctly inherited from ABC if inheritance chain includes an abstract model.""" + # Regression test for #11369: verbose_name_plural should be inherited + # from an ABC even when there are one or more intermediate + # abstract models in the inheritance chain, for consistency with + # verbose_name. + self.assertEquals( + InternalCertificationAudit._meta.verbose_name_plural, + u'Audits' + )