@@ -260,39 +260,50 @@ class InjectAnyAsBaseForNestedMeta(ModelClassInitializer):
260260
261261 @override
262262 def run (self ) -> None :
263- meta_node = helpers .get_nested_meta_node_for_current_class (
264- self .model_classdef .info
265- )
263+ # 1. Get the Meta node safely
264+ meta_node = helpers .get_nested_meta_node_for_current_class (self .model_classdef .info )
265+ if meta_node is None :
266+ if "Meta" in self .model_classdef .info .names :
267+ sym = self .model_classdef .info .names ["Meta" ]
268+ if sym and isinstance (sym .node , TypeInfo ):
269+ meta_node = sym .node
270+
266271 if meta_node is None :
267272 return None
268273
269- meta_node .fallback_to_any = True
270- typed_model_meta_info = self .lookup_typeinfo (
271- fullnames .TYPED_MODEL_META_FULLNAME
272- )
274+ # 2. Look up TypedModelMeta from django-stubs-ext
275+ typed_model_meta_info = self .lookup_typeinfo (fullnames .TYPED_MODEL_META_FULLNAME )
276+
277+ # If TypedModelMeta is not resolved, we fallback to Any to maintain compatibility
278+ if not typed_model_meta_info :
279+ meta_node .fallback_to_any = True
280+ return None
281+
282+ # 3. Validation Logic: Compare Meta attributes against TypedModelMeta
283+ meta_node .fallback_to_any = False
273284
274- if typed_model_meta_info :
275- for name , sym in meta_node . names . items ():
276- if name in typed_model_meta_info . names :
277- parent_sym = typed_model_meta_info . names [ name ]
285+ for name , sym in meta_node . names . items () :
286+ # Skip empty or uninitialized symbols to prevent internal errors
287+ if not sym or not sym . node or not hasattr ( sym , 'type' ) or sym . type is None :
288+ continue
278289
290+ # Only validate attributes defined in TypedModelMeta (e.g., verbose_name, db_table)
291+ if name in typed_model_meta_info .names :
292+ parent_sym = typed_model_meta_info .names [name ]
293+ if parent_sym and parent_sym .type :
279294 actual_type = get_proper_type (sym .type )
280295 expected_type = get_proper_type (parent_sym .type )
281-
282- if actual_type is None or expected_type is None :
283- if not self .api .final_iteration :
284- raise helpers .IncompleteDefnException ()
285- continue
286-
287- if not is_subtype (actual_type , expected_type ):
288- node_context = sym .node if sym .node is not None else self .ctx .api
289- error_context = cast (Context , node_context )
290-
291- self .ctx .api .fail (
292- f'Incompatible type for "{ name } " in Meta '
293- f'(expected "{ expected_type } ", got "{ actual_type } ")' ,
294- error_context ,
295- )
296+
297+ if actual_type and expected_type :
298+ # Check if the attribute type matches the expected Django type
299+ if not is_subtype (actual_type , expected_type ):
300+ self .api .fail (
301+ f'Incompatible type for "{ name } " in Meta (expected "{ expected_type } ", got "{ actual_type } ")' ,
302+ sym .node
303+ )
304+
305+ # 4. Re-enable fallback for custom/third-party Meta options
306+ meta_node .fallback_to_any = True
296307 return None
297308
298309class AddDefaultPrimaryKey (ModelClassInitializer ):
0 commit comments