跳转到主要内容

Django的Mypy存根

项目描述

django-stubs

Build status Checked with mypy Gitter StackOverflow

此包包含类型存根和自定义mypy插件,以提供更精确的Django框架静态类型和类型推断。Django使用一些Python“魔法”,这使得某些代码模式的精确类型变得有问题。这就是为什么我们需要这个项目。最终目标是能够为大多数常见模式获取精确的类型。

安装

pip install 'django-stubs[compatible-mypy]'

要使mypy意识到该插件,您需要在

[mypy]
plugins =
    mypy_django_plugin.main

[mypy.plugins.django-stubs]
django_settings_module = "myproject.settings"

您的mypy.inisetup.cfg 文件中添加。

也支持pyproject.toml配置

[tool.mypy]
plugins = ["mypy_django_plugin.main"]

[tool.django-stubs]
django_settings_module = "myproject.settings"

这里发生了两件事

  1. 我们需要显式列出要由mypy加载的插件
  2. 您可以选择指定如上所示的django_settings_module,或者让django_stubs使用您的环境中的DJANGO_SETTINGS_MODULE变量。

这个完全工作的类型模板可以作为示例。

版本兼容性

我们依赖于不同的djangomypy版本

django-stubs Mypy版本 Django版本 Django部分支持 Python版本
5.1.0 1.11.x 5.1 4.2 3.8 - 3.12
5.0.4 1.11.x 5.0 4.2 3.8 - 3.12
5.0.3 1.11.x 5.0 4.2 3.8 - 3.12
5.0.2 1.10.x 5.0 4.2 3.8 - 3.12
5.0.1 1.10.x 5.0 4.2 3.8 - 3.12
5.0.0 1.10.x 5.0 4.2, 4.1 3.8 - 3.12
4.2.7 1.7.x 4.2 4.1, 3.2 3.8 - 3.12
4.2.6 1.6.x 4.2 4.1, 3.2 3.8 - 3.12
4.2.5 1.6.x 4.2 4.1, 3.2 3.8 - 3.12
4.2.4 1.5.x 4.2 4.1, 3.2 3.8 - 3.11
4.2.3 1.4.x 4.2 4.1, 3.2 3.8 - 3.11
4.2.2 1.4.x 4.2 4.1, 3.2 3.8 - 3.11
4.2.1 1.3.x 4.2 4.1, 3.2 3.8 - 3.11
4.2.0 1.2.x 4.2 4.1, 4.0, 3.2 3.7 - 3.11
1.16.0 1.1.x 4.1 4.0, 3.2 3.7 - 3.11
1.15.0 1.0.x 4.1 4.0, 3.2 3.7 - 3.11
1.14.0 0.990+ 4.1 4.0, 3.2 3.7 - 3.11

功能

Model Meta属性的类型检查

通过从TypedModelMeta类继承,您可以确保您正在使用正确的类型属性

from django.db import models
from django_stubs_ext.db.models import TypedModelMeta

class MyModel(models.Model):
    example = models.CharField(max_length=100)

    class Meta(TypedModelMeta):
        ordering = ["example"]
        constraints = [
            models.UniqueConstraint(fields=["example"], name="unique_example"),
        ]

其他类型基类

  • django_stubs_ext.db.router.TypedDatabaseRouter可以用作实现自定义数据库路由时的基类。

设置

django-stubs有几个设置,您可以在

  • pyproject.toml中列出,在表[tool.django-stubs]
  • mypy.ini在表[mypy.plugins.django-stubs]

支持的设置有

  • django_settings_module,一个字符串,默认为os.getenv(DJANGO_SETTINGS_MODULE)

    指定您的设置模块的导入路径,与Django的DJANGO_SETTINGS_MODULE环境变量相同。

  • strict_settings,一个布尔值,默认为true

    如果使用动态设置,则设置为false,如下所述

常见问题解答

这是一个官方的Django项目吗?

不是,目前我们独立于Django。有一个提议将我们的项目合并到Django本身。您可以通过点赞PR来表示您的支持。

在生产中使用这个安全吗?

是的,它是安全的!此项目根本不会影响您的运行时。它只影响mypy类型检查过程。

但是,如果没有mypy,使用此项目就没有意义。

当我安装此插件运行mypy时,它会崩溃

当前的实现使用Django的运行时来提取有关模型的信息,所以如果您的已安装的应用程序或models.py损坏,它可能会崩溃。

换句话说,如果您的manage.py runserver崩溃,mypy也会崩溃。您也可以使用mypy--tb选项来获取有关错误的额外信息。

我无法使用带类型注解的QuerySet或Manager

当您尝试使用QuerySet[MyModel]Manager[MyModel]或其他基于Django的通用类型时,您将得到一个TypeError: 'type' object is not subscriptable错误。

这是因为这些Django类不支持运行时中的__class_getitem__魔法方法。

  1. 您可以使用我们的 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=[您的所需类型]) 添加额外的类型进行修补

  2. 您也可以使用字符串: 'QuerySet[MyModel]''Manager[MyModel]',这样它将作为 mypy 的类型在运行时作为常规 str

我如何创建一个保证有已认证用户的 HttpRequest 呢?

Django 内置的 HttpRequest 具有名为 user 的属性,该属性解析为类型

Union[User, AnonymousUser]

其中 User 是由 AUTH_USER_MODEL 设置指定的用户模型。

如果您想要一个您可以注解类型的 HttpRequest,并且您知道用户已认证,您可以像这样子类化正常的 HttpRequest

from django.http import HttpRequest
from my_user_app.models import MyUser


class AuthenticatedHttpRequest(HttpRequest):
    user: MyUser

然后使用 AuthenticatedHttpRequest 代替标准的 HttpRequest,在您知道用户已认证的情况下。例如在视图使用 @login_required 装饰器。

为什么我在自定义管理器上得到不兼容的返回类型错误?

如果您在没有泛型的情况下声明自定义管理器并重写内置方法,您可能会看到有关不兼容的错误消息,例如

from django.db import models

class MyManager(model.Manager):
    def create(self, **kwargs) -> "MyModel":
        pass

将导致此错误消息

错误:函数 "create" 的返回类型 "MyModel" 与超类型 "BaseManager" 中的 "_T" 不兼容

这是因为 Manager 类是泛型,但没有指定泛型,内置的管理器方法期望返回基管理器的泛型类型,即任何模型。要修复此问题,您应该声明您的管理器并使用您的模型作为类型变量

class MyManager(models.Manager["MyModel"]):
    ...

我如何注解我调用 QuerySet.annotate 的情况?

Django-stubs 提供了一个特殊类型,django_stubs_ext.WithAnnotations[Model, <Annotations>],表示 Model 已注解,意味着模型实例需要额外的属性。

您应该提供这些属性的 TypedDict,例如 WithAnnotations[MyModel, MyTypedDict],以指定哪些注解属性存在。

目前,mypy 插件可以识别传递给 QuerySet.annotate 的特定名称,并将它们包含在类型中,但不会记录这些属性的类型。

对于模型被注解的具体字段的了解尚未用于为 QuerySetvaluesvalues_listfilter 方法创建更具体的类型,但是了解模型已被注解的事实已被用于为 values/values_list 创建更广泛的结果类型,并允许对任何字段进行过滤。

from typing import TypedDict
from django_stubs_ext import WithAnnotations
from django.db import models
from django.db.models.expressions import Value


class MyModel(models.Model):
    username = models.CharField(max_length=100)


class MyTypedDict(TypedDict):
    foo: str


def func(m: WithAnnotations[MyModel, MyTypedDict]) -> str:
    print(m.bar)  # Error, since field "bar" is not in MyModel or MyTypedDict.
    return m.foo  # OK, since we said field "foo" was allowed


func(MyModel.objects.annotate(foo=Value("")).get(id=1))  # OK
func(MyModel.objects.annotate(bar=Value("")).get(id=1))  # Error

为什么我在不兼容的参数类型中提到 _StrPromise

Django 的懒翻译函数(如 gettext_lazy)返回一个 Promise 而不是 str。这两个类型 不能互换使用。因此,这些函数的返回类型已更改,以反映这一点。

如果您在自己的代码中遇到此错误,您可以将 Promise 强制转换为 str(导致翻译被评估),或者在使用类型提示时使用 django-stubs-ext 中的 StrPromiseStrOrPromise 类型。您选择的解决方案取决于具体情况。有关更多信息,请参阅 Django 文档中的 使用懒翻译对象

如果这是在 Django 代码中报告的,请报告问题或打开拉取请求以修复类型提示。

如何使用自定义库来处理 Django 设置?

使用类似于 django-split-settingsdjango-configurations 的东西将使 mypy 难以推断您的设置。

使用类似的情况也可能发生

try:
    from .local_settings import *
except Exception:
    pass

因此,mypy 不喜欢这段代码

from django.conf import settings

settings.CUSTOM_VALUE  # E: 'Settings' object has no attribute 'CUSTOM_VALUE'

为了处理这种特殊情况,我们有一个特殊的设置 strict_settings(默认值为 True),您可以将它切换到 False 以始终返回 Any 并不抛出任何错误,如果运行时设置模块有给定的值,例如 pyproject.toml

[tool.django-stubs]
strict_settings = false

或者 mypy.ini

[mypy.plugins.django-stubs]
strict_settings = false

然后

# Works:
reveal_type(settings.EXISTS_AT_RUNTIME)  # N: Any

# Errors:
reveal_type(settings.MISSING)  # E: 'Settings' object has no attribute 'MISSING'

相关项目

获取帮助

我们在这里有 Gitter: https://gitter.im/mypy-django/Lobby 如果您认为您有更通用的类型问题,请参考 https://github.com/python/mypy 和他们的 Gitter。

贡献

该项目是开源的,由社区驱动。因此,我们鼓励大家从小到大的贡献。您可以通过以下方式之一进行贡献

  1. 贡献代码(例如改进存根、添加插件功能、编写测试等) - 要这样做,请遵循 贡献指南
  2. 协助代码审查和问题讨论。
  3. 识别错误和问题,并报告这些问题
  4. StackOverflow 上提问和回答问题

您也可以在 gitter 上联系以讨论您的贡献!

项目详情


下载文件

下载您平台上的文件。如果您不确定要选择哪个,请了解有关 安装包 的更多信息。

源分发

django_stubs-5.1.0.tar.gz (265.8 KB 查看哈希

上传时间

构建分发

django_stubs-5.1.0-py3-none-any.whl (470.6 KB 查看哈希

上传于 Python 3

支持