@@ -18,6 +18,36 @@ def is_running_migration():
18
18
return False
19
19
20
20
21
+ def is_in_clear_cache ():
22
+ """
23
+ Check if the code is currently being called from Django's clear_cache() method.
24
+
25
+ TODO: This is fairly ugly, but in models.CustomObjectType.get_model() we call
26
+ meta = type() which calls clear_cache on the model which causes a call to
27
+ get_models() which in-turn calls get_model and therefore recurses.
28
+
29
+ This catches the specific case of a recursive call to get_models() from
30
+ clear_cache() which is the only case we care about, so should be relatively
31
+ safe. An alternative should be found for this.
32
+ """
33
+ import inspect
34
+
35
+ frame = inspect .currentframe ()
36
+ try :
37
+ # Walk up the call stack to see if we're being called from clear_cache
38
+ while frame :
39
+ if (
40
+ frame .f_code .co_name == "clear_cache"
41
+ and "django/apps/registry.py" in frame .f_code .co_filename
42
+ ):
43
+ return True
44
+ frame = frame .f_back
45
+ return False
46
+ finally :
47
+ # Clean up the frame reference
48
+ del frame
49
+
50
+
21
51
def check_custom_object_type_table_exists ():
22
52
"""
23
53
Check if the CustomObjectType table exists in the database.
@@ -49,6 +79,7 @@ class CustomObjectsPluginConfig(PluginConfig):
49
79
default_settings = {}
50
80
required_settings = []
51
81
template_extensions = "template_content.template_extensions"
82
+ _in_get_models = False # Recursion guard
52
83
53
84
def get_model (self , model_name , require_ready = True ):
54
85
try :
@@ -84,33 +115,45 @@ def get_model(self, model_name, require_ready=True):
84
115
85
116
def get_models (self , include_auto_created = False , include_swapped = False ):
86
117
"""Return all models for this plugin, including custom object type models."""
118
+
87
119
# Get the regular Django models first
88
120
for model in super ().get_models (include_auto_created , include_swapped ):
89
121
yield model
90
122
91
- # Suppress warnings about database calls during model loading
92
- with warnings .catch_warnings ():
93
- warnings .filterwarnings (
94
- "ignore" , category = RuntimeWarning , message = ".*database.*"
95
- )
96
- warnings .filterwarnings (
97
- "ignore" , category = UserWarning , message = ".*database.*"
98
- )
123
+ # Prevent recursion
124
+ if self ._in_get_models and is_in_clear_cache ():
125
+ # Skip dynamic model creation if we're in a recursive get_models call
126
+ return
99
127
100
- # Skip custom object type model loading if running during migration
101
- if is_running_migration () or not check_custom_object_type_table_exists ():
102
- return
103
-
104
- # Add custom object type models
105
- from .models import CustomObjectType
106
-
107
- custom_object_types = CustomObjectType .objects .all ()
108
- for custom_type in custom_object_types :
109
- # Only yield already cached models during discovery
110
- if CustomObjectType .is_model_cached (custom_type .id ):
111
- model = CustomObjectType .get_cached_model (custom_type .id )
128
+ self ._in_get_models = True
129
+ try :
130
+ # Suppress warnings about database calls during model loading
131
+ with warnings .catch_warnings ():
132
+ warnings .filterwarnings (
133
+ "ignore" , category = RuntimeWarning , message = ".*database.*"
134
+ )
135
+ warnings .filterwarnings (
136
+ "ignore" , category = UserWarning , message = ".*database.*"
137
+ )
138
+
139
+ # Skip custom object type model loading if running during migration
140
+ if (
141
+ is_running_migration ()
142
+ or not check_custom_object_type_table_exists ()
143
+ ):
144
+ return
145
+
146
+ # Add custom object type models
147
+ from .models import CustomObjectType
148
+
149
+ custom_object_types = CustomObjectType .objects .all ()
150
+ for custom_type in custom_object_types :
151
+ model = custom_type .get_model ()
112
152
if model :
113
153
yield model
154
+ finally :
155
+ # Clean up the recursion guard
156
+ self ._in_get_models = False
114
157
115
158
116
159
config = CustomObjectsPluginConfig
0 commit comments