diff --git a/django/db/models/options.py b/django/db/models/options.py index 0e229dea3a..109780ac49 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -100,6 +100,13 @@ class Options: "managers_map", "base_manager", "default_manager", + "db_returning_fields", + "_property_names", + "pk_fields", + "total_unique_constraints", + "all_parents", + "swapped", + "verbose_name_raw", } REVERSE_PROPERTIES = {"related_objects", "fields_map", "_relation_tree"} diff --git a/tests/apps/tests.py b/tests/apps/tests.py index 0f395b7fc3..ce7e19be1d 100644 --- a/tests/apps/tests.py +++ b/tests/apps/tests.py @@ -14,6 +14,7 @@ from django.test import ( skipUnlessDBFeature, ) from django.test.utils import extend_sys_path, isolate_apps +from django.utils.functional import cached_property from .models import SoAlternative, TotallyNormal, new_apps from .one_config_app.apps import OneConfig @@ -215,6 +216,27 @@ class AppsTests(SimpleTestCase): self.assertEqual(apps.get_swappable_settings_name.cache_info().currsize, 0) self.assertEqual(apps.get_models.cache_info().currsize, 0) + @override_settings(INSTALLED_APPS=SOME_INSTALLED_APPS) + def test_cached_properties_cleared_after_cache_clear(self): + opts = apps.get_model("admin", "LogEntry")._meta + + cached_properties = [ + name + for name, attr in models.options.Options.__dict__.items() + if isinstance(attr, cached_property) + ] + + # Access each cached property to populate the cache. + for attr_name in cached_properties: + getattr(opts, attr_name) + self.assertIn(attr_name, opts.__dict__) + + apps.clear_cache() + + for attr_name in cached_properties: + with self.subTest(property=attr_name): + self.assertNotIn(attr_name, opts.__dict__) + @override_settings(INSTALLED_APPS=["apps.apps.RelabeledAppsConfig"]) def test_relabeling(self): self.assertEqual(apps.get_app_config("relabeled").name, "apps")