Skip to content

Commit 60b96e6

Browse files
committed
Only populate a model argument for generic managers
1 parent faa5cc0 commit 60b96e6

File tree

4 files changed

+139
-21
lines changed

4 files changed

+139
-21
lines changed

mypy_django_plugin/transformers/models.py

+13-4
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,11 @@ def reparametrize_dynamically_created_manager(self, manager_name: str, manager_i
314314
return
315315

316316
assert manager_info is not None
317-
# Reparameterize dynamically created manager with model type
318-
manager_type = Instance(manager_info, [Instance(self.model_classdef.info, [])])
317+
if manager_info.is_generic():
318+
# Reparameterize dynamically created manager with model type
319+
manager_type = Instance(manager_info, [Instance(self.model_classdef.info, [])])
320+
else:
321+
manager_type = Instance(manager_info, [])
319322
self.add_new_node_to_model_class(manager_name, manager_type, is_classvar=True)
320323

321324
def run_with_model_cls(self, model_cls: Type[Model]) -> None:
@@ -340,7 +343,10 @@ def run_with_model_cls(self, model_cls: Type[Model]) -> None:
340343
incomplete_manager_defs.add(manager_name)
341344
continue
342345

343-
manager_type = Instance(manager_info, [Instance(self.model_classdef.info, [])])
346+
if manager_info.is_generic():
347+
manager_type = Instance(manager_info, [Instance(self.model_classdef.info, [])])
348+
else:
349+
manager_type = Instance(manager_info, [])
344350
self.add_new_node_to_model_class(manager_name, manager_type, is_classvar=True)
345351

346352
if incomplete_manager_defs:
@@ -439,7 +445,10 @@ def run_with_model_cls(self, model_cls: Type[Model]) -> None:
439445
return None
440446
default_manager_info = generated_manager_info
441447

442-
default_manager = Instance(default_manager_info, [Instance(self.model_classdef.info, [])])
448+
if default_manager_info.is_generic():
449+
default_manager = Instance(default_manager_info, [Instance(self.model_classdef.info, [])])
450+
else:
451+
default_manager = Instance(default_manager_info, [])
443452
self.add_new_node_to_model_class("_default_manager", default_manager, is_classvar=True)
444453

445454

tests/typecheck/managers/querysets/test_from_queryset.yml

+96-7
Original file line numberDiff line numberDiff line change
@@ -438,14 +438,14 @@
438438
- case: test_queryset_in_model_class_body
439439
main: |
440440
from myapp.models import MyModel
441-
reveal_type(MyModel.objects) # N: Revealed type is "myapp.models.MyManagerFromMyQuerySet[myapp.models.MyModel]"
442-
reveal_type(MyModel._default_manager) # N: Revealed type is "myapp.models.MyManagerFromMyQuerySet[myapp.models.MyModel]"
443-
reveal_type(MyModel.objects.all) # N: Revealed type is "def () -> myapp.models.MyQuerySet[myapp.models.MyModel]"
441+
reveal_type(MyModel.objects) # N: Revealed type is "myapp.models.MyManagerFromMyQuerySet"
442+
reveal_type(MyModel._default_manager) # N: Revealed type is "myapp.models.MyManagerFromMyQuerySet"
443+
reveal_type(MyModel.objects.all) # N: Revealed type is "def () -> myapp.models.MyQuerySet"
444444
reveal_type(MyModel.objects.custom) # N: Revealed type is "def () -> myapp.models.MyQuerySet"
445-
reveal_type(MyModel.objects.all().filter) # N: Revealed type is "def (*args: Any, **kwargs: Any) -> myapp.models.MyQuerySet[myapp.models.MyModel]"
445+
reveal_type(MyModel.objects.all().filter) # N: Revealed type is "def (*args: Any, **kwargs: Any) -> myapp.models.MyQuerySet"
446446
reveal_type(MyModel.objects.custom().filter) # N: Revealed type is "def (*args: Any, **kwargs: Any) -> myapp.models.MyQuerySet"
447-
reveal_type(MyModel.objects2) # N: Revealed type is "myapp.models.MyManagerFromMyQuerySet[myapp.models.MyModel]"
448-
reveal_type(MyModel._default_manager) # N: Revealed type is "myapp.models.MyManagerFromMyQuerySet[myapp.models.MyModel]"
447+
reveal_type(MyModel.objects2) # N: Revealed type is "myapp.models.MyManagerFromMyQuerySet"
448+
reveal_type(MyModel._default_manager) # N: Revealed type is "myapp.models.MyManagerFromMyQuerySet"
449449
installed_apps:
450450
- myapp
451451
files:
@@ -468,7 +468,7 @@
468468
- case: test_queryset_in_model_class_body_subclass
469469
main: |
470470
from myapp.models import MyModel
471-
reveal_type(MyModel.objects) # N: Revealed type is "myapp.models.BaseManagerFromBaseQuerySet[myapp.models.MyModel]"
471+
reveal_type(MyModel.objects) # N: Revealed type is "myapp.models.BaseManagerFromBaseQuerySet"
472472
installed_apps:
473473
- myapp
474474
files:
@@ -675,3 +675,92 @@
675675
...
676676
677677
StrCallable = BaseManager.from_queryset(ModelQuerySet, class_name=str(1))
678+
679+
- case: only_auto_populates_model_arg_to_manager_with_type_var
680+
main: |
681+
from myapp.models import MyModel
682+
reveal_type(MyModel.bm_populated_from_qs_populated)
683+
reveal_type(MyModel.bm_generic_from_qs_populated)
684+
685+
reveal_type(MyModel.bm_populated_from_qs_generic)
686+
reveal_type(MyModel.bm_generic_from_qs_generic)
687+
688+
reveal_type(MyModel.m_populated_from_qs_populated)
689+
reveal_type(MyModel.m_generic_from_qs_populated)
690+
691+
reveal_type(MyModel.m_populated_from_qs_generic)
692+
reveal_type(MyModel.m_generic_from_qs_generic)
693+
694+
reveal_type(MyModel.qs_populated_as_manager)
695+
reveal_type(MyModel.qs_generic_as_manager)
696+
out: |
697+
main:2: note: Revealed type is "myapp.models.BaseManagerPopulatedFromQuerySetPopulated"
698+
main:3: note: Revealed type is "myapp.models.BaseManagerGenericFromQuerySetPopulated[myapp.models.MyModel]"
699+
main:5: note: Revealed type is "myapp.models.BaseManagerPopulatedFromQuerySetGeneric"
700+
main:6: note: Revealed type is "myapp.models.BaseManagerGenericFromQuerySetGeneric[myapp.models.MyModel]"
701+
main:8: note: Revealed type is "myapp.models.ManagerPopulatedFromQuerySetPopulated"
702+
main:9: note: Revealed type is "myapp.models.ManagerGenericFromQuerySetPopulated[myapp.models.MyModel]"
703+
main:11: note: Revealed type is "myapp.models.ManagerPopulatedFromQuerySetGeneric"
704+
main:12: note: Revealed type is "myapp.models.ManagerGenericFromQuerySetGeneric[myapp.models.MyModel]"
705+
main:14: note: Revealed type is "myapp.models.ManagerFromQuerySetPopulated[myapp.models.MyModel]"
706+
# TODO: We want a line like below and not an error.. But mypy doesn't trigger
707+
# the dynamic class hook for classes with generic arguments..
708+
# Revealed type is "myapp.models.ManagerFromQuerySetGeneric[myapp.models.MyModel]"
709+
main:15: note: Revealed type is "myapp.models.UnknownManager[myapp.models.MyModel]"
710+
myapp/models:50: error: Could not resolve manager type for "myapp.models.MyModel.qs_generic_as_manager"
711+
installed_apps:
712+
- myapp
713+
files:
714+
- path: myapp/__init__.py
715+
- path: myapp/models.py
716+
content: |
717+
from typing import TypeVar
718+
from django.db.models import Manager, Model, QuerySet
719+
from django.db.models.manager import BaseManager
720+
721+
class QuerySetPopulated(QuerySet["MyModel"]):
722+
...
723+
724+
T = TypeVar("T", bound=Model)
725+
class QuerySetGeneric(QuerySet[T]):
726+
...
727+
728+
class BaseManagerPopulated(BaseManager["MyModel"]):
729+
...
730+
731+
class BaseManagerGeneric(BaseManager[T]):
732+
...
733+
734+
class ManagerPopulated(Manager["MyModel"]):
735+
...
736+
737+
class ManagerGeneric(Manager[T]):
738+
...
739+
740+
BaseManagerPopulatedFromQuerySetPopulated = BaseManagerPopulated.from_queryset(QuerySetPopulated)
741+
BaseManagerGenericFromQuerySetPopulated = BaseManagerGeneric.from_queryset(QuerySetPopulated)
742+
743+
BaseManagerPopulatedFromQuerySetGeneric = BaseManagerPopulated.from_queryset(QuerySetGeneric)
744+
BaseManagerGenericFromQuerySetGeneric = BaseManagerGeneric.from_queryset(QuerySetGeneric)
745+
746+
ManagerPopulatedFromQuerySetPopulated = ManagerPopulated.from_queryset(QuerySetPopulated)
747+
ManagerGenericFromQuerySetPopulated = ManagerGeneric.from_queryset(QuerySetPopulated)
748+
749+
ManagerPopulatedFromQuerySetGeneric = ManagerPopulated.from_queryset(QuerySetGeneric)
750+
ManagerGenericFromQuerySetGeneric = ManagerGeneric.from_queryset(QuerySetGeneric)
751+
752+
class MyModel(Model):
753+
bm_populated_from_qs_populated = BaseManagerPopulatedFromQuerySetPopulated()
754+
bm_generic_from_qs_populated = BaseManagerGenericFromQuerySetPopulated()
755+
756+
bm_populated_from_qs_generic = BaseManagerPopulatedFromQuerySetGeneric()
757+
bm_generic_from_qs_generic = BaseManagerGenericFromQuerySetGeneric()
758+
759+
m_populated_from_qs_populated = ManagerPopulatedFromQuerySetPopulated()
760+
m_generic_from_qs_populated = ManagerGenericFromQuerySetPopulated()
761+
762+
m_populated_from_qs_generic = ManagerPopulatedFromQuerySetGeneric()
763+
m_generic_from_qs_generic = ManagerGenericFromQuerySetGeneric()
764+
765+
qs_populated_as_manager = QuerySetPopulated.as_manager()
766+
qs_generic_as_manager = QuerySetGeneric["MyModel"].as_manager()

tests/typecheck/managers/test_managers.yml

+29-9
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@
126126
- case: test_leave_as_is_if_objects_is_set_and_fill_typevars_with_outer_class
127127
main: |
128128
from myapp.models import MyUser
129-
reveal_type(MyUser.objects) # N: Revealed type is "myapp.models.UserManager[myapp.models.MyUser]"
129+
reveal_type(MyUser.objects) # N: Revealed type is "myapp.models.UserManager"
130130
reveal_type(MyUser.objects.get()) # N: Revealed type is "myapp.models.MyUser"
131131
reveal_type(MyUser.objects.get_or_404()) # N: Revealed type is "myapp.models.MyUser"
132132
installed_apps:
@@ -169,9 +169,9 @@
169169
main: |
170170
from myapp.models import AbstractPerson, Book
171171
reveal_type(AbstractPerson.abstract_persons) # N: Revealed type is "django.db.models.manager.Manager[myapp.models.AbstractPerson]"
172-
reveal_type(Book.published_objects) # N: Revealed type is "myapp.models.PublishedBookManager[myapp.models.Book]"
172+
reveal_type(Book.published_objects) # N: Revealed type is "myapp.models.PublishedBookManager"
173173
Book.published_objects.create(title='hello')
174-
reveal_type(Book.annotated_objects) # N: Revealed type is "myapp.models.AnnotatedBookManager[myapp.models.Book]"
174+
reveal_type(Book.annotated_objects) # N: Revealed type is "myapp.models.AnnotatedBookManager"
175175
Book.annotated_objects.create(title='hello')
176176
installed_apps:
177177
- myapp
@@ -196,20 +196,33 @@
196196
main: |
197197
from myapp.models import AbstractBase1, AbstractBase2, Child
198198
reveal_type(Child.manager1)
199+
reveal_type(Child.manager1.get())
199200
reveal_type(Child.restricted)
201+
reveal_type(Child.restricted.get())
202+
reveal_type(Child.manager3)
203+
reveal_type(Child.manager3.get())
200204
reveal_type(AbstractBase1.manager1)
205+
reveal_type(AbstractBase1.manager1.get())
201206
reveal_type(AbstractBase2.restricted)
207+
reveal_type(AbstractBase2.restricted.get())
202208
out: |
203-
main:2: note: Revealed type is "myapp.models.CustomManager1[myapp.models.Child]"
204-
main:3: note: Revealed type is "myapp.models.CustomManager2[myapp.models.Child]"
205-
main:4: note: Revealed type is "myapp.models.CustomManager1[myapp.models.AbstractBase1]"
206-
main:5: note: Revealed type is "myapp.models.CustomManager2[myapp.models.AbstractBase2]"
209+
main:2: note: Revealed type is "myapp.models.CustomManager1"
210+
main:3: note: Revealed type is "myapp.models.AbstractBase1"
211+
main:4: note: Revealed type is "myapp.models.CustomManager2"
212+
main:5: note: Revealed type is "myapp.models.AbstractBase2"
213+
main:6: note: Revealed type is "myapp.models.GenericManager[myapp.models.Child]"
214+
main:7: note: Revealed type is "myapp.models.Child"
215+
main:8: note: Revealed type is "myapp.models.CustomManager1"
216+
main:9: note: Revealed type is "myapp.models.AbstractBase1"
217+
main:10: note: Revealed type is "myapp.models.CustomManager2"
218+
main:11: note: Revealed type is "myapp.models.AbstractBase2"
207219
installed_apps:
208220
- myapp
209221
files:
210222
- path: myapp/__init__.py
211223
- path: myapp/models.py
212224
content: |
225+
from typing import TypeVar
213226
from django.db import models
214227
class CustomManager1(models.Manager['AbstractBase1']):
215228
pass
@@ -225,8 +238,15 @@
225238
abstract = True
226239
value = models.CharField(max_length=50)
227240
restricted = CustomManager2()
241+
T = TypeVar("T", bound="AbstractBase3")
242+
class GenericManager(models.Manager[T]):
243+
...
244+
class AbstractBase3(models.Model):
245+
class Meta:
246+
abstract = True
247+
manager3 = GenericManager()
228248
229-
class Child(AbstractBase1, AbstractBase2):
249+
class Child(AbstractBase1, AbstractBase2, AbstractBase3):
230250
pass
231251
232252
- case: model_has_a_manager_of_the_same_type
@@ -639,7 +659,7 @@
639659
installed_apps:
640660
- myapp
641661
out: |
642-
main:2: note: Revealed type is "myapp.models.MyModel.MyManager[myapp.models.MyModel]"
662+
main:2: note: Revealed type is "myapp.models.MyModel.MyManager"
643663
files:
644664
- path: myapp/__init__.py
645665
- path: myapp/models.py

tests/typecheck/models/test_contrib_models.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
- case: can_override_abstract_user_manager
3333
main: |
3434
from myapp.models import MyBaseUser, MyUser
35-
reveal_type(MyBaseUser.objects) # N: Revealed type is "myapp.models.MyBaseUserManager[myapp.models.MyBaseUser]"
35+
reveal_type(MyBaseUser.objects) # N: Revealed type is "myapp.models.MyBaseUserManager"
3636
reveal_type(MyBaseUser.objects.all()) # N: Revealed type is "django.db.models.query._QuerySet[myapp.models.MyBaseUser, myapp.models.MyBaseUser]"
3737
reveal_type(MyUser.objects) # N: Revealed type is "myapp.models.MyUserManager"
3838
reveal_type(MyUser.objects.all()) # N: Revealed type is "django.db.models.query._QuerySet[myapp.models.MyUser, myapp.models.MyUser]"

0 commit comments

Comments
 (0)