@@ -91,6 +91,35 @@ class LookupsAreUnsupported(Exception):
9191 pass
9292
9393
94+ def _get_field_type_from_model_type_info (info : Optional [TypeInfo ], field_name : str ) -> Optional [Instance ]:
95+ if info is None :
96+ return None
97+ field_node = info .get (field_name )
98+ if field_node is None or not isinstance (field_node .type , Instance ):
99+ return None
100+ # Field declares a set and a get type arg. Fallback to `None` when we can't find any args
101+ elif len (field_node .type .args ) != 2 :
102+ return None
103+ else :
104+ return field_node .type
105+
106+
107+ def _get_field_set_type_from_model_type_info (info : Optional [TypeInfo ], field_name : str ) -> Optional [MypyType ]:
108+ field_type = _get_field_type_from_model_type_info (info , field_name )
109+ if field_type is not None :
110+ return field_type .args [0 ]
111+ else :
112+ return None
113+
114+
115+ def _get_field_get_type_from_model_type_info (info : Optional [TypeInfo ], field_name : str ) -> Optional [MypyType ]:
116+ field_type = _get_field_type_from_model_type_info (info , field_name )
117+ if field_type is not None :
118+ return field_type .args [1 ]
119+ else :
120+ return None
121+
122+
94123class DjangoContext :
95124 def __init__ (self , django_settings_module : str ) -> None :
96125 self .django_settings_module = django_settings_module
@@ -152,13 +181,13 @@ def get_field_lookup_exact_type(
152181 ) -> MypyType :
153182 if isinstance (field , (RelatedField , ForeignObjectRel )):
154183 related_model_cls = self .get_field_related_model_cls (field )
155- primary_key_field = self .get_primary_key_field (related_model_cls )
156- primary_key_type = self .get_field_get_type (api , primary_key_field , method = "init" )
157-
158184 rel_model_info = helpers .lookup_class_typeinfo (api , related_model_cls )
159185 if rel_model_info is None :
160186 return AnyType (TypeOfAny .explicit )
161187
188+ primary_key_field = self .get_primary_key_field (related_model_cls )
189+ primary_key_type = self .get_field_get_type (api , rel_model_info , primary_key_field , method = "init" )
190+
162191 model_and_primary_key_type = UnionType .make_union ([Instance (rel_model_info , []), primary_key_type ])
163192 return helpers .make_optional (model_and_primary_key_type )
164193
@@ -200,19 +229,6 @@ def get_expected_types(self, api: TypeChecker, model_cls: Type[Model], *, method
200229 field_set_type = self .get_field_set_type (api , primary_key_field , method = method )
201230 expected_types ["pk" ] = field_set_type
202231
203- def get_field_set_type_from_model_type_info (info : Optional [TypeInfo ], field_name : str ) -> Optional [MypyType ]:
204- if info is None :
205- return None
206- field_node = info .get (field_name )
207- if field_node is None or not isinstance (field_node .type , Instance ):
208- return None
209- elif not field_node .type .args :
210- # Field declares a set and a get type arg. Fallback to `None` when we can't find any args
211- return None
212-
213- set_type = field_node .type .args [0 ]
214- return set_type
215-
216232 model_info = helpers .lookup_class_typeinfo (api , model_cls )
217233 for field in model_cls ._meta .get_fields ():
218234 if isinstance (field , Field ):
@@ -223,7 +239,7 @@ def get_field_set_type_from_model_type_info(info: Optional[TypeInfo], field_name
223239 # Try to retrieve set type from a model's TypeInfo object and fallback to retrieving it manually
224240 # from django-stubs own declaration. This is to align with the setter types declared for
225241 # assignment.
226- field_set_type = get_field_set_type_from_model_type_info (
242+ field_set_type = _get_field_set_type_from_model_type_info (
227243 model_info , field_name
228244 ) or self .get_field_set_type (api , field , method = method )
229245 expected_types [field_name ] = field_set_type
@@ -340,20 +356,31 @@ def get_field_set_type(
340356 return field_set_type
341357
342358 def get_field_get_type (
343- self , api : TypeChecker , field : Union ["Field[Any, Any]" , ForeignObjectRel ], * , method : str
359+ self ,
360+ api : TypeChecker ,
361+ model_info : Optional [TypeInfo ],
362+ field : Union ["Field[Any, Any]" , ForeignObjectRel ],
363+ * ,
364+ method : str ,
344365 ) -> MypyType :
345366 """Get a type of __get__ for this specific Django field."""
367+ if isinstance (field , Field ):
368+ get_type = _get_field_get_type_from_model_type_info (model_info , field .attname )
369+ if get_type is not None :
370+ return get_type
371+
346372 field_info = helpers .lookup_class_typeinfo (api , field .__class__ )
347373 if field_info is None :
348374 return AnyType (TypeOfAny .unannotated )
349375
350376 is_nullable = self .get_field_nullability (field , method )
351377 if isinstance (field , RelatedField ):
352378 related_model_cls = self .get_field_related_model_cls (field )
379+ rel_model_info = helpers .lookup_class_typeinfo (api , related_model_cls )
353380
354381 if method in ("values" , "values_list" ):
355382 primary_key_field = self .get_primary_key_field (related_model_cls )
356- return self .get_field_get_type (api , primary_key_field , method = method )
383+ return self .get_field_get_type (api , rel_model_info , primary_key_field , method = method )
357384
358385 model_info = helpers .lookup_class_typeinfo (api , related_model_cls )
359386 if model_info is None :
0 commit comments