Skip to content

Commit c313cfd

Browse files
authored
Fix IndexError crash when using from_queryset() of custom Manager subclass (#1786)
1 parent 7c058b8 commit c313cfd

File tree

2 files changed

+48
-7
lines changed

2 files changed

+48
-7
lines changed

mypy_django_plugin/transformers/managers.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,17 @@ def _process_dynamic_method(
125125
variables = []
126126
args_types = method_type.arg_types[1:]
127127
if _has_compatible_type_vars(base_that_has_method):
128-
ret_type = _replace_type_var(
129-
ret_type, base_that_has_method.defn.type_vars[0].fullname, manager_instance.args[0]
130-
)
131-
args_types = [
132-
_replace_type_var(arg_type, base_that_has_method.defn.type_vars[0].fullname, manager_instance.args[0])
133-
for arg_type in args_types
134-
]
128+
typed_var = manager_instance.args or queryset_info.bases[0].args
129+
if (
130+
typed_var
131+
and isinstance(typed_var[0], Instance)
132+
and typed_var[0].type.has_base(fullnames.MODEL_CLASS_FULLNAME)
133+
):
134+
ret_type = _replace_type_var(ret_type, base_that_has_method.defn.type_vars[0].fullname, typed_var[0])
135+
args_types = [
136+
_replace_type_var(arg_type, base_that_has_method.defn.type_vars[0].fullname, manager_instance.args[0])
137+
for arg_type in args_types
138+
]
135139
if base_that_has_method.self_type:
136140
# Manages -> Self returns
137141
ret_type = _replace_type_var(ret_type, base_that_has_method.self_type.fullname, manager_instance)

tests/typecheck/managers/querysets/test_from_queryset.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,43 @@
9393
NewManager = BaseManager.from_queryset(ModelQuerySet)
9494
class MyModel(models.Model):
9595
objects = NewManager()
96+
97+
- case: from_queryset_custom_manager_subclass
98+
main: |
99+
from myapp.models import NewManager
100+
reveal_type(NewManager().example()) # N: Revealed type is "myapp.models.MyModel"
101+
installed_apps:
102+
- myapp
103+
files:
104+
- path: myapp/__init__.py
105+
- path: myapp/querysets.py
106+
content: |
107+
from typing import TypeVar, TYPE_CHECKING
108+
109+
from django.db import models
110+
from django.db.models.manager import BaseManager
111+
if TYPE_CHECKING:
112+
from .models import MyModel
113+
114+
_CTE = TypeVar("_CTE", bound=models.Model)
115+
116+
class _MyModelQuerySet(models.QuerySet[_CTE]):
117+
118+
def example(self) -> _CTE: ...
119+
120+
class MyModelQuerySet(_MyModelQuerySet["MyModel"]): ...
121+
122+
- path: myapp/models.py
123+
content: |
124+
from django.db import models
125+
from django.db.models.manager import BaseManager
126+
from .querysets import MyModelQuerySet
127+
128+
class TypedManager(BaseManager["MyModel"]): ...
129+
130+
NewManager = TypedManager.from_queryset(MyModelQuerySet)
131+
class MyModel(models.Model):
132+
objects = NewManager()
96133
- case: handles_subclasses_of_queryset
97134
main: |
98135
from myapp.models import MyModel

0 commit comments

Comments
 (0)