跳转到主要内容

Django 的 Mypy stubs

项目描述

sentry-forked-django-stubs

新版本发布

为上游标签的分支创建一个新的分支

git remote add upstream git@github.com:typeddjango/django-stubs
git fetch upstream --tags
git push origin --tags
git checkout 1.2.3 -b sentry-1.2.3
  • master 分支 cherry-pick craft / release 提交到您的分支中
  • 从先前版本 cherry-pick 相关提交

发布通过 release.yml 工作流程中的 craft 完成 -- 确保使用带有 .# 发布后缀的特定分支(如 1.2.3.0)进行目标定位


django-stubs

Build status Checked with mypy Gitter StackOverflow

此软件包包含 类型 stubs 和自定义 mypy 插件,以提供更精确的静态类型和类型推断。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本身中。您可以通过点赞PR来表示您的支持。

在生产中使用它安全吗?

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

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

安装此插件时,mypy崩溃

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

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

我无法使用带有类型注解的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=[YourDesiredType])添加要修复的额外类型。

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

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

Django内置的HttpRequest具有一个属性user,它解析为类型

Union[User, AnonymousUser]

其中UserAUTH_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,并允许对任何字段进行 filter

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 上联系以讨论您的贡献!

项目详情


下载文件

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

源代码分发

sentry_forked_django_stubs-5.1.0.post2.tar.gz (266.4 kB 查看散列)

上传时间 源代码

构建分发

sentry_forked_django_stubs-5.1.0.post2-py3-none-any.whl (471.0 kB 查看散列)

上传时间 Python 3

由以下支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误日志 StatusPage StatusPage 状态页面