如何让Django、类型注解、Flake8协同工作?

How to make Django, type annotations, and Flake8 work together?

我正在使用 Django/DRF。我想写类型检查 Python。我也想要linting。

Django 的模型结构包括为每个模型创建一个管理器 class,并将管理器 class 的实例分配给模型的 objects 成员。

由于我使用的是类型注释,因此管理器 class 的方法现在还包含对模型的引用。这导致 flake8 报告一个或另一个错误。

这是一个例子:

class UserManager:

    def create_user(email: str) -> User:
        ...                        ^^^^ [flake8] F821: undefined name 'User'

class User:

    objects = UserManager()

如果我翻转它们,我会得到:

class User:

    objects = UserManager()
              ^^^^^^^^^^^ [flake8] F821: undefined name 'UserManager'

class UserManager:

    def create_user(email: str) -> User:
        ...                        

人们如何编写类型化、linted 的 Django 代码?

除了打字之外请注意:Django 的模型不要求您为每个模型都有一个明确的管理器! Django 在每个模型上使用 objects 属性 生成一个管理器。您只需要为特定用途添加自定义 ModelManager。转到类型...

我认为如果您实际使用 UserManager,您应该将它放在 User 之前 class。对于尚未定义的名称,您可以将类型用引号引起来,PEP 484 under the "Forward References" section 的文档中对此进行了介绍。

class UserManager:

    def create_user(email: str) -> "User":
        ...                        

class User:

    objects = UserManager()

另一种选择是在注释中使用 Python 2 语法定义类型提示, 显示了类似的答案。

class UserManager:

    def create_user(email: str):
        # type: (str) -> User
        ...

在 Python 3.7 中,您可以使用 from __future__ import annotations 修复此问题,这将允许包含前向引用的类型注释。对于更简单的代码重构和 linting,这比使用字符串注释(如另一个答案所建议的)更可取。使用引号只是不支持前向引用注释的 Python 旧版本的解决方法。在 Python 4(如果不是更早的话)中,这将是默认行为。