Repository: django/django
Issue category: Performance Issue

Issue:
Cache Options.verbose_name_raw
Description
		
(last modified by Adam Johnson)		
Another candidate for caching, like #35230, following the same system check profiling.
The Model._meta.verbose_name_raw property returns the stringified version of the verbose_name attribute whilst temporarily disabling translations. It is only used in django.contrib.auth for creating permission names.
I found this property was taking ~15% of the total runtime for system checks on a project with 118 models. calls. Turning it into a cached_property and adding a no-translation fast path saves nearly all this cost, with the below results.
Before: 520 calls taking 10ms
After: 105 calls taking ~0ms


Hints:

Patch:
diff --git a/django/db/models/options.py b/django/db/models/options.py
index 9b3106f67ef6..e63a81c2d8e7 100644
--- a/django/db/models/options.py
+++ b/django/db/models/options.py
@@ -395,9 +395,11 @@ def can_migrate(self, connection):
             )
         return True
 
-    @property
+    @cached_property
     def verbose_name_raw(self):
         """Return the untranslated verbose name."""
+        if isinstance(self.verbose_name, str):
+            return self.verbose_name
         with override(None):
             return str(self.verbose_name)
 


Test patch:
diff --git a/tests/model_meta/models.py b/tests/model_meta/models.py
index 6da62be2ac26..bc69d61a59cd 100644
--- a/tests/model_meta/models.py
+++ b/tests/model_meta/models.py
@@ -1,6 +1,7 @@
 from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
 from django.contrib.contenttypes.models import ContentType
 from django.db import models
+from django.utils.translation import gettext_lazy as _
 
 
 class Relation(models.Model):
@@ -124,6 +125,9 @@ class Person(BasePerson):
     # GR fields
     generic_relation_concrete = GenericRelation(Relation)
 
+    class Meta:
+        verbose_name = _("Person")
+
 
 class ProxyPerson(Person):
     class Meta:
diff --git a/tests/model_meta/tests.py b/tests/model_meta/tests.py
index fe2f6e63da8d..7204a5e93a14 100644
--- a/tests/model_meta/tests.py
+++ b/tests/model_meta/tests.py
@@ -222,6 +222,17 @@ def test_get_fields_only_searches_forward_on_apps_not_ready(self):
             opts.apps.models_ready = True
 
 
+class VerboseNameRawTests(SimpleTestCase):
+    def test_string(self):
+        # Clear cached property.
+        Relation._meta.__dict__.pop("verbose_name_raw", None)
+        self.assertEqual(Relation._meta.verbose_name_raw, "relation")
+
+    def test_gettext(self):
+        Person._meta.__dict__.pop("verbose_name_raw", None)
+        self.assertEqual(Person._meta.verbose_name_raw, "Person")
+
+
 class RelationTreeTests(SimpleTestCase):
     all_models = (Relation, AbstractPerson, BasePerson, Person, ProxyPerson, Relating)