Django模型的类型注释

42

我正在开发一个 Django 项目。由于这是一个新项目,我希望使用 Python 3.6+ 类型注释对其进行全面注释。 我试图注释模型,但我很难找到一个好的方法。

IntegerField 为例,我看到了两种注释的选择:

# number 1
int_field: int = models.IntegerField()

# number 2
int_field: models.IntegerField = models.IntegerField()

我的Mypy测试中排名第一失败:

Incompatible types in assignment (expression has type "IntegerField[<nothing>, <nothing>]", variable has type "int")

2号方案对于mypy来说是可以接受的,但像PyCharm这样的IDE无法解析它,并且经常抱怨使用了错误的类型。

是否有任何最佳实践来正确注释模型,以满足mypy和IDE的要求?


3
既然你似乎花了很多时间来满足工具,那么听到这个消息或许会让你感到惊讶:你所做的事情,并不是类型注释设计的用途——静态类型。 - Klaus D.
你难道不是在寻找类似 mypy-django 这样的东西吗? - Pedram
1
@KlausD. - 我不确定我理解你的意思,你能详细说明一下吗? - Djent
1
@wowkin2 PyCharm只是抱怨一下,例如当一个函数使用int参数,而我将使用模型中的字段时。然后函数调用会被突出显示,PyCharm会说“预期为'int',得到'IntegerField'”。 - Djent
2
@aaron:不,因为那肯定是错误的。问题在于Django模型是一种非常不同的东西;类包含字段,实例包含具体值。类型会随着上下文而改变,实例没有IntegerField,但如果使用Union,那么访问该字段的所有代码都必须考虑到可能性。 - Martijn Pieters
显示剩余6条评论
1个回答

62

Django模型(和其他组件)很难注释,因为它们背后有很多奇妙的技术,好消息是一群酷炫的开发者已经替我们做了艰苦的工作。

django-stubs提供了一组存根文件和mypy插件,为Django提供静态类型和类型推断。

例如,有以下模型:

from django.contrib.auth import get_user_model
from django.db import models

User = get_user_model()

class Post(models.Model):
    title = models.CharField(max_length=255)
    pubdate = models.DateTimeField()
    author = models.ForeignKey(User, on_delete=models.CASCADE)

mypy 会抱怨并说:

demo$ mypy .
demo/models.py:9: error: Need type annotation for 'title'
demo/models.py:10: error: Need type annotation for 'pubdate'
demo/models.py:11: error: Need type annotation for 'author'
Found 3 errors in 1 file (checked 5 source files)
安装该软件包即可解决问题。
pip install django-stubs

并创建一个setup.cfg文件,其中包含以下内容:

[mypy]
mypy_path = ./demo
plugins =
    mypy_django_plugin.main

strict_optional = True

[mypy.plugins.django-stubs]
django_settings_module = demo.settings

(不要忘记根据您的设置模块的名称更新mypy_pathdjango_settings_module指向的目录)

完成此操作后,mypy将能够推断和检查Django模型(以及其他组件)的注释。

demo$ mypy .
Success: no issues found in 5 source files

以下是小视图中使用的示例:

from django.db.models.query import QuerySet
from django.http import HttpRequest, HttpResponse
from django.shortcuts import render

from demo.models import Post

def _get_posts() -> 'QuerySet[Post]':
    return Post.objects.all()

def posts(request: HttpRequest, template: str='posts.html') -> HttpResponse:
    return render(request, template, {'posts': _get_posts()})

再次提醒,mypy对提供的注释感到满意:

demo$ mypy .
Success: no issues found in 7 source files

同样的,Django Rest Framework 的一个包也可用: djangorestframework-stubs


我确实找到了这个,但是我用错了。谢谢你详细的回答,帮助我继续前进。 - Djent
看起来插件也试图执行Django项目本身,因此如果它依赖于特定的环境或类似情况,则无法运行。 - WhyNotHugo
1
由于答案没有明确提到这一点,而它是问题的一部分,因此在PyCharm中安装django-stubs将修复PyCharm对类型的投诉。 PyCharm内置的Django支持可以正确处理大多数Django事物的类型,但在将字段用作其值时会失败。 - Wooble
djangorestframework-stubs 链接是一个分支,请使用原始仓库。 - Danilo Gómez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接