Make SomeModel._default_manager return a BaseManager[SomeModel] instead 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 <michael.poehle@polyteia.de>
This commit is contained in:
emma-blossom
2022-01-28 19:42:11 +01:00
committed by GitHub
parent 060dc3b41a
commit 73290597f8
3 changed files with 44 additions and 8 deletions

View File

@@ -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

View File

@@ -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]: ...

View File

@@ -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