55from django .db .models .fields .related import ForeignKey
66from django .db .models .fields .reverse_related import ManyToManyRel , ManyToOneRel , OneToOneRel
77from mypy .checker import TypeChecker
8- from mypy .nodes import ARG_STAR2 , Argument , Context , FuncDef , TypeInfo , Var
8+ from mypy .nodes import ARG_STAR2 , Argument , AssignmentStmt , Context , FuncDef , NameExpr , TypeInfo , Var
99from mypy .plugin import AnalyzeTypeContext , AttributeContext , CheckerPluginInterface , ClassDefContext
1010from mypy .plugins import common
1111from mypy .semanal import SemanticAnalyzer
@@ -234,7 +234,7 @@ def create_new_model_parametrized_manager(self, name: str, base_manager_info: Ty
234234 def run_with_model_cls (self , model_cls : Type [Model ]) -> None :
235235 manager_info : Optional [TypeInfo ]
236236
237- encountered_incomplete_manager_def = False
237+ incomplete_manager_defs = set ()
238238 for manager_name , manager in model_cls ._meta .managers_map .items ():
239239 manager_class_name = manager .__class__ .__name__
240240 manager_fullname = helpers .get_class_fullname (manager .__class__ )
@@ -243,13 +243,11 @@ def run_with_model_cls(self, model_cls: Type[Model]) -> None:
243243 except helpers .IncompleteDefnException as exc :
244244 # Check if manager is a generated (dynamic class) manager
245245 base_manager_fullname = helpers .get_class_fullname (manager .__class__ .__bases__ [0 ])
246- manager_info = self .get_generated_manager_info (manager_fullname , base_manager_fullname )
247- if manager_info is None :
246+ if manager_fullname not in self .get_generated_manager_mappings (base_manager_fullname ):
248247 # Manager doesn't appear to be generated. Track that we encountered an
249248 # incomplete definition and skip
250- encountered_incomplete_manager_def = True
251- continue
252- _ , manager_class_name = manager_info .fullname .rsplit ("." , maxsplit = 1 )
249+ incomplete_manager_defs .add (manager_name )
250+ continue
253251
254252 if manager_name not in self .model_classdef .info .names :
255253 manager_type = Instance (manager_info , [Instance (self .model_classdef .info , [])])
@@ -275,9 +273,38 @@ def run_with_model_cls(self, model_cls: Type[Model]) -> None:
275273
276274 self .add_new_node_to_model_class (manager_name , custom_manager_type )
277275
278- if encountered_incomplete_manager_def and not self .api .final_iteration :
279- # Unless we're on the final round, see if another round could figuring out all manager types
276+ if incomplete_manager_defs and not self .api .final_iteration :
277+ # Unless we're on the final round, see if another round could figure out all manager types
280278 raise helpers .IncompleteDefnException ()
279+ elif self .api .final_iteration :
280+ for manager_name in incomplete_manager_defs :
281+ # We act graceful and set the type as the bare minimum we know of
282+ # (Django's default) before finishing. And emit an error, to allow for
283+ # ignoring a more specialised manager not being resolved while still
284+ # setting _some_ type
285+ django_manager_info = self .lookup_typeinfo (fullnames .MANAGER_CLASS_FULLNAME )
286+ assert (
287+ django_manager_info is not None
288+ ), f"Type info for Django's { fullnames .MANAGER_CLASS_FULLNAME } missing"
289+ self .add_new_node_to_model_class (
290+ manager_name , Instance (django_manager_info , [Instance (self .model_classdef .info , [])])
291+ )
292+ # Find expression for e.g. `objects = SomeManager()`
293+ manager_expr = [
294+ expr
295+ for expr in self .ctx .cls .defs .body
296+ if (
297+ isinstance (expr , AssignmentStmt )
298+ and isinstance (expr .lvalues [0 ], NameExpr )
299+ and expr .lvalues [0 ].name == manager_name
300+ )
301+ ]
302+ manager_fullname = f"{ self .model_classdef .fullname } .{ manager_name } "
303+ self .api .fail (
304+ f'Could not resolve manager type for "{ manager_fullname } "' ,
305+ manager_expr [0 ] if manager_expr else self .ctx .cls ,
306+ code = MANAGER_MISSING ,
307+ )
281308
282309
283310class AddDefaultManagerAttribute (ModelClassInitializer ):
0 commit comments