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.core.exceptions import ObjectDoesNotExist, ValidationError
from django.db.models.manager import BaseManager from django.db.models.manager import BaseManager
from django.db.models.options import Options from django.db.models.options import Options
from django.utils.functional import classproperty
_Self = TypeVar("_Self", bound="Model") _Self = TypeVar("_Self", bound="Model")
@@ -22,8 +23,12 @@ class Model(metaclass=ModelBase):
class MultipleObjectsReturned(BaseMultipleObjectsReturned): ... class MultipleObjectsReturned(BaseMultipleObjectsReturned): ...
class Meta: ... class Meta: ...
_meta: Options[Any] _meta: Options[Any]
_default_manager: BaseManager[Model] @classproperty
_base_manager: BaseManager[Model] @classmethod
def _default_manager(cls: Type[_Self]) -> BaseManager[_Self]: ...
@classproperty
@classmethod
def _base_manager(cls: Type[_Self]) -> BaseManager[_Self]: ...
objects: BaseManager[Any] objects: BaseManager[Any]
pk: Any = ... pk: Any = ...
_state: ModelState _state: ModelState

View File

@@ -62,8 +62,14 @@ def partition(
predicate: Callable, values: List[_PartitionMember] predicate: Callable, values: List[_PartitionMember]
) -> Tuple[List[_PartitionMember], List[_PartitionMember]]: ... ) -> Tuple[List[_PartitionMember], List[_PartitionMember]]: ...
class classproperty: _Get = TypeVar("_Get")
fget: Optional[Callable] = ... _Self = TypeVar("_Self")
def __init__(self, method: Optional[Callable] = ...) -> None: ...
def __get__(self, instance: Any, cls: Optional[type] = ...) -> Any: ... class classproperty(Generic[_Get]):
def getter(self, method: Callable) -> classproperty: ... 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]): class Base(Generic[_T]):
def __init__(self, model_cls: Type[_T]): def __init__(self, model_cls: Type[_T]):
self.model_cls = model_cls 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): class MyModel(models.Model):
pass pass
class Child(Base[MyModel]): class Child(Base[MyModel]):
def method(self) -> None: def method(self) -> None:
reveal_type(self.model_cls._default_manager) # N: Revealed type is "django.db.models.manager.Manager[myapp.models.MyModel]" 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 - case: if_custom_manager_defined_it_is_set_to_default_manager
main: | main: |
from myapp.models import MyModel from myapp.models import MyModel