Mypy:在访问 super() 上的方法时正确键入 Django mixin 类

问题描述 投票:0回答:1

Django 有一个怪癖,它在写入数据库之前默认不验证模型。开发人员尝试通过创建 Mixin 类来解决一种不理想的情况,例如: https://www.xormedia.com/django-model-validation-on-save/

该解决方法的想法是,每当您想要向其添加保存时验证时,都可以将此 mixin 继承到自定义定义的 Django 模型中。

这种方法有效,但我正在尝试将这些旧示例升级为正确键入的示例。以下是我当前的代码:

from typing import Iterable, TypeVar

from django.db import models

DjangoModel = TypeVar("DjangoModel", bound=models.Model)

class ValidateModelMixin:
    """Use this mixing to make model.save() call model.full_clean()

    Django's model.save() doesn't call full_clean() by default. More info:
    * "Why doesn't django's model.save() call full clean?"
        http://stackoverflow.com/questions/4441539/
    * "Model docs imply that ModelForm will call Model.full_clean(),
        but it won't."
        https://code.djangoproject.com/ticket/13100
    """

    def save(
        self: DjangoModel,
        force_insert: bool = False,
        force_update: bool = False,
        using: str | None = "default",  # DEFAULT_DB_ALIAS
        update_fields: Iterable[str] | None = None,
    ) -> None:
        """Override the save method to call full_clean before saving the model.

        Takes into account the force_insert and force_update flags, as they
        are passed to the save method when trying to skip the validation.
        Also passes on any positional and keyword arguments that were passed
        at the original call-site of the method.
        """
        # Only validate the model if the force-flags are not enabled
        if not (force_insert or force_update):
            self.full_clean()

        # Then save the model, passing in the original arguments
        super().save(force_insert, force_update, using, update_fields)

Mypy 对于上面的代码产生以下错误:

错误:“super”不支持参数 2 [杂项]

我认为这是 Mypy 不喜欢我传递给

super().save()
的论点。但这些论点似乎确实与 Django 的
models.Model.save
的论点相匹配: https://docs.djangoproject.com/en/5.0/ref/models/instances/#django.db.models.Model.save https://github.com/typeddjango/django-stubs/blob/1546e5c78aae6974f93d1c2c29ba50c177187f3a/django-stubs/db/models/base.pyi#L72

我相信我可能没有为 self 参数设置正确的类型,但我不确定应该如何输入这段代码。

python django mypy
1个回答
0
投票

我结束了这个其他问题帮助了我。具体来说:

mypy 建议通过协议来实现 mixins

因此,我实现了一个协议来定义 DjangoModel 的类型,并使类型检查通过。我最终使用的完整代码:

from typing import Iterable, Protocol, Self


class DjangoModel(Protocol):
    def save(
        self: Self,
        force_insert: bool = False,
        force_update: bool = False,
        using: str | None = "default",
        update_fields: Iterable[str] | None = None,
    ) -> None: ...

    def full_clean(
        self,
        exclude: Iterable[str] | None = None,
        validate_unique: bool = True,
        validate_constraints: bool = True,
    ) -> None: ...


class ValidateModelMixin:
    """Make model.save() call model.full_clean()

    Django's model.save() doesn't call full_clean() by default. More info:
    * "Why doesn't django's model.save() call full clean?"
        http://stackoverflow.com/questions/4441539/

    * "Model docs imply that ModelForm will call Model.full_clean(),
        but it won't."
        https://code.djangoproject.com/ticket/13100
    """

    def save(
        self: DjangoModel,
        force_insert: bool = False,
        force_update: bool = False,
        using: str | None = "default",  # DEFAULT_DB_ALIAS
        update_fields: Iterable[str] | None = None,
    ) -> None:
        """Override the save method to call full_clean before saving the model.

        Takes into account the force_insert and force_update flags, as they
        are passed to the save method when trying to skip the validation.
        Also passes on any positional and keyword arguments that were passed
        at the original call-site of the method.
        """
        # Only validate the model if the force-flags are not enabled
        if not (force_insert or force_update):
            self.full_clean()

        # Then save the model, passing in the original arguments
        super().save(force_insert, force_update, using, update_fields)

© www.soinside.com 2019 - 2024. All rights reserved.