Skip to content

Commit

Permalink
Make SomeModel._default_manager return a BaseManager[SomeModel] inste…
Browse files Browse the repository at this point in the history
…ad of BaseManager[Model] (#817)

* _default_manager has more specific type for TypeVars

* remove unnecessary # type: ignore

* add test for _base_manager

* add overloads for classproperty.__get__

* readd # type: ignore for WSGIRequestHandler.connection, fails in github's pipeline

* fix _base_manager test: mypy reveals an inferred type

Co-authored-by: Michael Pöhle <[email protected]>
  • Loading branch information
emma-blossom and Michael Pöhle authored Jan 28, 2022
1 parent 060dc3b commit 7329059
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 8 deletions.
9 changes: 7 additions & 2 deletions django-stubs/db/models/base.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand All @@ -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
Expand Down
16 changes: 11 additions & 5 deletions django-stubs/utils/functional.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -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]: ...
27 changes: 26 additions & 1 deletion tests/typecheck/managers/test_managers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 7329059

Please sign in to comment.