diff --git a/django-stubs/db/models/base.pyi b/django-stubs/db/models/base.pyi index db494b5a7..2c1860bf1 100644 --- a/django-stubs/db/models/base.pyi +++ b/django-stubs/db/models/base.pyi @@ -5,6 +5,7 @@ from django.core.exceptions import MultipleObjectsReturned as BaseMultipleObject from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.db.models.manager import BaseManager from django.db.models.options import Options +from django.utils.functional import classproperty _Self = TypeVar("_Self", bound="Model") @@ -22,8 +23,12 @@ class Model(metaclass=ModelBase): class MultipleObjectsReturned(BaseMultipleObjectsReturned): ... class Meta: ... _meta: Options[Any] - _default_manager: BaseManager[Model] - _base_manager: BaseManager[Model] + @classproperty + @classmethod + def _default_manager(cls: Type[_Self]) -> BaseManager[_Self]: ... + @classproperty + @classmethod + def _base_manager(cls: Type[_Self]) -> BaseManager[_Self]: ... objects: BaseManager[Any] pk: Any = ... _state: ModelState diff --git a/django-stubs/utils/functional.pyi b/django-stubs/utils/functional.pyi index c2d9b18a1..da7114849 100644 --- a/django-stubs/utils/functional.pyi +++ b/django-stubs/utils/functional.pyi @@ -62,8 +62,14 @@ def partition( predicate: Callable, values: List[_PartitionMember] ) -> Tuple[List[_PartitionMember], List[_PartitionMember]]: ... -class classproperty: - fget: Optional[Callable] = ... - def __init__(self, method: Optional[Callable] = ...) -> None: ... - def __get__(self, instance: Any, cls: Optional[type] = ...) -> Any: ... - def getter(self, method: Callable) -> classproperty: ... +_Get = TypeVar("_Get") +_Self = TypeVar("_Self") + +class classproperty(Generic[_Get]): + fget: Optional[Callable[[Type[_Self]], _Get]] = ... + def __init__(self, method: Optional[Callable[[Type[_Self]], _Get]] = ...) -> None: ... + @overload + def __get__(self, instance: None, cls: Type[_Self] = ...) -> _Get: ... + @overload + def __get__(self, instance: _Self, cls: Type[_Self] = ...) -> _Get: ... + def getter(self, method: Callable[[Type[_Self]], _Get]) -> classproperty[_Get]: ... diff --git a/tests/typecheck/managers/test_managers.yml b/tests/typecheck/managers/test_managers.yml index 709dbeb54..9ee3eb388 100644 --- a/tests/typecheck/managers/test_managers.yml +++ b/tests/typecheck/managers/test_managers.yml @@ -48,13 +48,38 @@ class Base(Generic[_T]): def __init__(self, model_cls: Type[_T]): self.model_cls = model_cls - reveal_type(self.model_cls._default_manager) # N: Revealed type is "django.db.models.manager.BaseManager[django.db.models.base.Model]" + reveal_type(self.model_cls._default_manager) # N: Revealed type is "django.db.models.manager.BaseManager[_T`1]" class MyModel(models.Model): pass class Child(Base[MyModel]): def method(self) -> None: reveal_type(self.model_cls._default_manager) # N: Revealed type is "django.db.models.manager.Manager[myapp.models.MyModel]" +- case: test_base_manager_called_on_model_cls_as_generic_parameter + main: | + from myapp.models import Base, MyModel + base_instance = Base(MyModel) + reveal_type(base_instance.model_cls._base_manager) # N: Revealed type is "django.db.models.manager.BaseManager[myapp.models.MyModel*]" + installed_apps: + - myapp + files: + - path: myapp/__init__.py + - path: myapp/models.py + content: | + from typing import TypeVar, Generic, Type + from django.db import models + + _T = TypeVar('_T', bound=models.Model) + class Base(Generic[_T]): + def __init__(self, model_cls: Type[_T]): + self.model_cls = model_cls + reveal_type(self.model_cls._base_manager) # N: Revealed type is "django.db.models.manager.BaseManager[_T`1]" + class MyModel(models.Model): + pass + class Child(Base[MyModel]): + def method(self) -> None: + reveal_type(self.model_cls._base_manager) # N: Revealed type is "django.db.models.manager.BaseManager[myapp.models.MyModel*]" + - case: if_custom_manager_defined_it_is_set_to_default_manager main: | from myapp.models import MyModel