Django的类型存根
项目描述
django-types 
Django的类型存根。
注意:该项目是从https://github.com/typeddjango/django-stubs分叉的,目的是移除对
mypy
插件的依赖,这样mypy
不会因为Django配置而崩溃,并且非mypy
类型检查器,如pyright
,将与Django更好地协同工作。
安装
pip install django-types
当您尝试使用QuerySet[MyModel]
、Manager[MyModel]
或其他基于Django的通用类型时,您将得到TypeError: 'type' object is not subscriptable
错误。
这种情况发生是因为这些Django类不支持运行时中的__class_getitem__
魔法方法。
-
您可以使用
django_stubs_ext
辅助工具,它会修补我们在django中使用的所有类型。安装它
pip install django-stubs-ext # as a production dependency
然后将它放置在您的顶级设置中
import django_stubs_ext django_stubs_ext.monkeypatch()
您可以使用
django_stubs_ext.monkeypatch(extra_classes=[YourDesiredType])
添加额外的类型来修补 -
您也可以使用字符串:
'QuerySet[MyModel]'
和'Manager[MyModel]'
,这样它就可以作为类型检查的类型,并在运行时作为常规的str
。
用法
ORM模型中的外键ids和相关名称作为属性
当定义一个具有外键的Django ORM模型时,如下所示
class User(models.Model):
team = models.ForeignKey(
"Team",
null=True,
on_delete=models.SET_NULL,
)
role = models.ForeignKey(
"Role",
null=True,
on_delete=models.SET_NULL,
related_name="users",
)
将创建两个属性,即预期的team
和team_id
。另外,在Team
上创建了一个相关管理器user_set
,用于反向访问。
为了正确地为外键本身以及创建的ids添加类型,您可以这样做
from typing import TYPE_CHECKING
from someapp.models import Team
if TYPE_CHECKING:
# In this example Role cannot be imported due to circular import issues,
# but doing so inside TYPE_CHECKING will make sure that the typing below
# knows what "Role" means
from anotherapp.models import Role
class User(models.Model):
team_id: Optional[int]
team = models.ForeignKey(
Team,
null=True,
on_delete=models.SET_NULL,
)
role_id: int
role = models.ForeignKey["Role"](
"Role",
null=False,
on_delete=models.SET_NULL,
related_name="users",
)
reveal_type(User().team)
# note: Revealed type is 'Optional[Team]'
reveal_type(User().role)
# note: Revealed type is 'Role'
这将确保可以访问team_id
和role_id
。此外,team
和role
将被类型化为它们相应的对象。
要能够访问相关管理器Team
和Role
,您可以这样做
from typing import TYPE_CHECKING
if TYPE_CHECKING:
# This doesn't really exists on django so it always need to be imported this way
from django.db.models.manager import RelatedManager
from user.models import User
class Team(models.Model):
if TYPE_CHECKING:
user_set = RelatedManager["User"]()
class Role(models.Model):
if TYPE_CHECKING:
users = RelatedManager["User"]()
reveal_type(Team().user_set)
# note: Revealed type is 'RelatedManager[User]'
reveal_type(Role().users)
# note: Revealed type is 'RelatedManager[User]'
另一种选择是使用注解
from __future__ import annotations # or just be in python 3.11
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from django.db.models import Manager
from user.models import User
class Team(models.Model):
user_set: Manager[User]
class Role(models.Model):
users: Manager[User]
reveal_type(Team().user_set)
# note: Revealed type is 'Manager[User]'
reveal_type(Role().users)
# note: Revealed type is 'Manager[User]'
id字段
默认情况下,如果不存在,Django会为您创建一个AutoField
。
为了让类型检查器知道关于id
字段的信息,您需要显式地声明该字段。
# before
class Post(models.Model):
...
# after
class Post(models.Model):
id = models.AutoField(primary_key=True)
# OR
id: int
HttpRequest
的user
属性
HttpRequest
的user
属性的类型为Union[AbstractBaseUser, AnonymousUser]
,但对于您的大多数视图,您可能只想有一个认证用户或一个AnonymousUser
。
因此,我们可以为每种情况定义一个子类
class AuthedHttpRequest(HttpRequest):
user: User # type: ignore [assignment]
然后您可以在视图中使用它
@auth.login_required
def activity(request: AuthedHttpRequest, team_id: str) -> HttpResponse:
...
您还可以通过使login_required
装饰器更严格来确保其装饰的函数的第一个参数是AuthedHttpRequest
from typing import Any, Union, TypeVar, cast
from django.http import HttpRequest, HttpResponse
from typing_extensions import Protocol
from functools import wraps
class RequestHandler1(Protocol):
def __call__(self, request: AuthedHttpRequest) -> HttpResponse:
...
class RequestHandler2(Protocol):
def __call__(self, request: AuthedHttpRequest, __arg1: Any) -> HttpResponse:
...
RequestHandler = Union[RequestHandler1, RequestHandler2]
# Verbose bound arg due to limitations of Python typing.
# see: https://github.com/python/mypy/issues/5876
_F = TypeVar("_F", bound=RequestHandler)
def login_required(view_func: _F) -> _F:
@wraps(view_func)
def wrapped_view(
request: AuthedHttpRequest, *args: object, **kwargs: object
) -> HttpResponse:
if request.user.is_authenticated:
return view_func(request, *args, **kwargs) # type: ignore [call-arg]
raise AuthenticationRequired
return cast(_F, wrapped_view)
然后下面的将会产生类型错误
@auth.login_required
def activity(request: HttpRequest, team_id: str) -> HttpResponse:
...
相关
项目详情
下载文件
下载您平台上的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。
源分布
构建分布
django_types-0.19.1.tar.gz 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 5ae7988612cf6fbc357b018bbc3b3a878b65e04275cc46e0d35d66a708daff12 |
|
MD5 | 81c194a77cde8319230568c0aea9ba74 |
|
BLAKE2b-256 | a16d52b9bba1390645d1593152336dc1afbf1cbcfe1fcec78e3ed4eaee09f3ff |
django_types-0.19.1-py3-none-any.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | b3f529de17f6374d41ca67232aa01330c531bbbaa3ac4097896f31ac33c96c30 |
|
MD5 | 8eb624adb9881e72880b280452d00c4d |
|
BLAKE2b-256 | 25cbd088c67245a9d5759a08dbafb47e040ee436e06ee433a3cdc7f3233b3313 |