3535 orm_lookups ,
3636 querysets ,
3737 settings ,
38+ storage ,
3839)
3940from mypy_django_plugin .transformers .auth import get_user_model
4041from mypy_django_plugin .transformers .functional import resolve_str_promise_attribute
@@ -98,8 +99,29 @@ def get_additional_deps(self, file: MypyFile) -> list[tuple[int, str, int]]:
9899 if file .fullname == "django.conf" and self .django_context .django_settings_module :
99100 return [self ._new_dependency (self .django_context .django_settings_module , PRI_MED )]
100101
102+ # for settings.STORAGES["staticfiles"]
103+ if (
104+ file .fullname == "django.contrib.staticfiles.storage"
105+ and isinstance (storage_config := self .django_context .settings .STORAGES .get ("staticfiles" ), dict )
106+ and isinstance (storage_backend := storage_config .get ("BACKEND" ), str )
107+ and "." in storage_backend
108+ ):
109+ return [self ._new_dependency (storage_backend .rsplit ("." , 1 )[0 ])]
110+
111+ # for settings.STORAGES
112+ elif file .fullname == "django.core.files.storage" :
113+ return [
114+ self ._new_dependency (storage_backend .rsplit ("." , 1 )[0 ])
115+ for storage_config in self .django_context .settings .STORAGES .values ()
116+ if (
117+ isinstance (storage_config , dict )
118+ and isinstance (storage_backend := storage_config .get ("BACKEND" ), str )
119+ and "." in storage_backend
120+ )
121+ ]
122+
101123 # for values / values_list
102- if file .fullname == "django.db.models" :
124+ elif file .fullname == "django.db.models" :
103125 return [self ._new_dependency ("typing" ), self ._new_dependency ("django_stubs_ext" )]
104126
105127 # for `get_user_model()`
@@ -200,6 +222,9 @@ def get_method_hook(self, fullname: str) -> Callable[[MethodContext], MypyType]
200222 }
201223 return hooks .get (class_fullname )
202224
225+ elif method_name == "__getitem__" and class_fullname == fullnames .STORAGE_HANDLER_CLASS_FULLNAME :
226+ return partial (storage .extract_proper_type_for_getitem , django_context = self .django_context )
227+
203228 if method_name in self .manager_and_queryset_method_hooks :
204229 info = self ._get_typeinfo_or_none (class_fullname )
205230 if info and helpers .has_any_of_bases (
@@ -298,6 +323,10 @@ def get_type_analyze_hook(self, fullname: str) -> Callable[[AnalyzeTypeContext],
298323 return partial (handle_annotated_type , fullname = fullname )
299324 elif fullname == "django.contrib.auth.models._User" :
300325 return partial (get_user_model , django_context = self .django_context )
326+ elif fullname == "django.contrib.staticfiles.storage._ConfiguredStorage" :
327+ return partial (storage .get_storage , alias = "staticfiles" , django_context = self .django_context )
328+ elif fullname == "django.core.files.storage._DefaultStorage" :
329+ return partial (storage .get_storage , alias = "default" , django_context = self .django_context )
301330 return None
302331
303332 def get_dynamic_class_hook (self , fullname : str ) -> Callable [[DynamicClassDefContext ], None ] | None :
@@ -311,9 +340,20 @@ def get_dynamic_class_hook(self, fullname: str) -> Callable[[DynamicClassDefCont
311340
312341 def report_config_data (self , ctx : ReportConfigContext ) -> dict [str , Any ]:
313342 # Cache would be cleared if any settings do change.
314- extra_data = {}
315- # In all places we use '_User' alias as a type we want to clear cache if
316- # AUTH_USER_MODEL setting changes
343+ extra_data : dict [str , Any ] = {}
344+ # In all places we use '_DefaultStorage' or '_ConfiguredStorage' aliases as a type we want to clear the cache
345+ # if STORAGES setting changes
346+ if ctx .id .startswith ("django.contrib.staticfiles" ) or ctx .id .startswith ("django.core.files.storage" ):
347+ extra_data ["STORAGES" ] = [
348+ storage_backend
349+ for storage_config in self .django_context .settings .STORAGES .values ()
350+ if (
351+ isinstance (storage_config , dict )
352+ and isinstance (storage_backend := storage_config .get ("BACKEND" ), str )
353+ and "." in storage_backend
354+ )
355+ ]
356+ # In all places we use '_User' alias as a type we want to clear the cache if AUTH_USER_MODEL setting changes
317357 if ctx .id .startswith ("django.contrib.auth" ) or ctx .id in {"django.http.request" , "django.test.client" }:
318358 extra_data ["AUTH_USER_MODEL" ] = self .django_context .settings .AUTH_USER_MODEL
319359 return self .plugin_config .to_json (extra_data )
0 commit comments