sun*_*unw 5 python django mypy django-stubs
我有一个现有的 Django 项目,它使用User扩展的自定义模型类AbstractUser。由于各种重要原因,我们需要重新定义该email字段,如下:
class User(AbstractUser):
...
email = models.EmailField(db_index=True, blank=True, null=True, unique=True)
...
Run Code Online (Sandbox Code Playgroud)
最近添加了通过 mypy 进行键入检查。但是,当我执行 mypy 检查时,出现以下错误:
错误:赋值中的类型不兼容(表达式的类型为“EmailField[str | int | Combinable | None, str | None]”,基类“AbstractUser”将类型定义为“EmailField[str | int | Combinable, str]”)[任务]
我怎样才能让 mypy 允许这种类型重新分配?我不想仅仅使用,# type: ignore因为我希望使用它的类型保护。
对于上下文,如果我确实使用# type: ignore,那么我会从我的代码库中得到数十个以下 mypy 错误的实例:
错误:无法确定“电子邮件”的类型 [has-type]
以下是我的设置的详细信息:
python version: 3.10.5
django version: 3.2.19
mypy version: 1.6.1
django-stubs[compatible-mypy] version: 4.2.6
django-stubs-ext version: 4.2.5
typing-extensions version: 4.8.0
Run Code Online (Sandbox Code Playgroud)
您可以尝试使用与基类兼容的类型注释AbstractUser,但也允许自定义模型中的字段None值。emailUser
from typing import Optional
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
email: Optional[models.EmailField] = models.EmailField(db_index=True, blank=True, null=True, unique=True)
Run Code Online (Sandbox Code Playgroud)
email如果您显式地将字段注释为Optional[models.EmailField],则表明可以email是models.EmailField或类型None,这应该满足 Django 和 mypy 的要求。
通过使用Optional类型注释,您可以保持自定义模型的完整性User,同时也遵守 mypy 的类型检查标准。不再出现“不兼容类型”错误。
Incompatible types in assignment (expression has type "EmailField[Any, Any] | None", base class "AbstractUser" defined the type as "EmailField[str | int | Combinable, str]") [assignment]
email这意味着自定义模型中字段的类型定义User与类期望的类型不完全兼容AbstractUser。鉴于 mypy 对类型一致性非常严格,尤其是在覆盖子类中的字段时,您需要确保您的字段定义与预期类型匹配,AbstractUser同时还允许None.
创建一个类型变量,其中包含 允许的类型AbstractUser以及您的附加要求None。
并在字段定义中使用此类型变量来满足基类和您的自定义要求。
from typing import Union, TypeVar
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.db.models.fields import Combinable
# Define a TypeVar that includes the types from AbstractUser and None
EmailFieldType = TypeVar("EmailFieldType", models.EmailField, None)
class User(AbstractUser):
email: Union[EmailFieldType, models.EmailField[str | int | Combinable, str]] = models.EmailField(db_index=True, blank=True, null=True, unique=True)
Run Code Online (Sandbox Code Playgroud)
EmailFieldType现在是一个类型变量,允许email字段为 amodels.EmailField或None,与 中指定的类型对齐AbstractUser。在字段定义中使用Union应该满足 的特定类型要求AbstractUser以及您的自定义模型允许的需要None。
Run Code Online (Sandbox Code Playgroud)error: Type variable "myproject.models.User.EmailFieldType" is unbound`
TypeVar您可以直接使用Union来指定字段可以是或email所需的类型,而不是使用 a 。这将简化类型定义,并且对于 mypy 来说应该更容易解释。AbstractUserNone
error: Type variable "myproject.models.User.EmailFieldType" is unbound`
Run Code Online (Sandbox Code Playgroud)
emailmodels.EmailField字段被声明为和的并集None。这应该满足类型要求,AbstractUser同时也允许None作为值。
不幸的是同样的问题再次发生
然后,您将需要更复杂的类型处理或符合 mypy 期望的解决方法,而不损害 Django 模型的完整性。
尝试创建一个自定义子类models.EmailField,以与 Django 和 mypy 兼容的方式显式处理可为 null 的场景。
from typing import Union
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.db.models.fields import Combinable
class User(AbstractUser):
email: Union[models.EmailField, None] = models.EmailField(db_index=True, blank=True, null=True, unique=True)
Run Code Online (Sandbox Code Playgroud)
作为更严厉的措施,您可以尝试完全重新定义该领域并谨慎处理迁移。这种方法需要email从模型中删除该字段,然后使用新规范重新添加它。这更具侵入性,需要仔细的迁移处理。
| 归档时间: |
|
| 查看次数: |
506 次 |
| 最近记录: |