根据另一个字段的值验证 Django 模型字段?

Posted

技术标签:

【中文标题】根据另一个字段的值验证 Django 模型字段?【英文标题】:Validating a Django model field based on another field's value? 【发布时间】:2017-02-28 02:51:15 【问题描述】:

我有一个 Django 应用程序,其模型可由 Django REST 框架和常规表单界面访问。表单界面在保存对模型的更改之前进行了一些验证检查,但没有使用任何特殊的 Django 框架,只是在视图中进行了简单的本地更改。

我想对表单和 REST 调用应用相同的验证,因此我想将验证移到模型中。我可以使用字段的验证器字段查看如何在简单情况下执行此操作,但在一种情况下,我有一个名称/类型/值模型,其中“值”的可接受值根据选择的类型而变化。验证器不会收到有关该字段所在模型的任何信息,因此它无权访问其他字段。

如何在 DRF 的序列化程序和表单的 POST 视图中没有基本相同的代码的情况下执行此验证?

【问题讨论】:

这个section from the Django docs 可能会有所帮助,尽管它不处理 REST 部分。 或Model.clean 在某些情况下可能会这样做。 【参考方案1】:

我稍微研究了一下 drf 的代码库。您可以使用以下方法获取所有字段的值。这样做,您可以将序列化错误抛出为 'my_field':'error message 而不是 'non_field_error':'error message'

def validate_myfield(self, value):
   data = self.get_initial() # data for all the fields
   #do your validation

但是,如果您希望为 ListSerializer 执行此操作,即为 serializer = serializer_class(many=True),这将不起作用。您将获得空值列表。 在这种情况下,您可以在 def validate 函数中编写验证,并在序列化错误中避免 non_field_errors,您可以将错误消息作为字典而不是字符串引发 ValidationError

def validate(self, data):
    # do your validation
    raise serializers.ValidationError("your_field": "error_message")
 

【讨论】:

这正是我所需要的。为什么其他人要尝试如此困难的方法?这必须是被接受的答案。 我得到验证器()缺少 1 个必需的位置参数:“值”。我现在如何调用验证器函数?【参考方案2】:

当每个字段的验证定义如下时,不会发送任何有关其他字段的信息:

def validate_myfield(self, value):
    ...

但是,如果您有这样定义的方法:

def validate(self, data):
    ...

然后你得到一个字典中的所有数据,你可以做跨领域验证。

【讨论】:

这个答案是在 DRF ModelSerializer 子类的上下文中。尽管 Django 模型中存在类似的情况,但对于 clean_myfieldclean 方法。您是否通过 DRF 发布...? 是和不是。我有一个 DRF api,还有一个表单发布到的视图。 为什么不在表单的 clean 中使用 ModelSerializer 实例?这会将验证码保存在一个位置。 它不是一个模型表单,只是一对视图函数(显示表单和处理帖子)......我想我可能会让我的生活变得比必要的更难 :-) 肯定有一些选择ModelSerializer 是唯一的验证点。 您不必实际发布到 API 即可使用 ModelSerializer。您也可以只导入并实例化一个序列化程序以将其用于验证。虽然这有点不寻常,但它是可能的,而且实际上在测试中经常有用。【参考方案3】:

您可以使用required 包进行跨字段验证。它允许您在 python 中以声明方式表达您的验证规则。你会在 DRF 中得到这样的结果:

class MySerializer(serializers.Serializer):

    REQUIREMENTS = (
        Requires("end_date", "start_date") +
        Requires("end_date", R("end_date") > R("start_date")) + 
        Requires("end_date", R("end_date") < today.date() + one_year) + 
        Requires("start_date", R("start_date") < today.date() + one_year)
     )

    start_date = serializers.DateField(required=False, null=True, blank=True)
    end_date = serializers.DateField(required=False, null=True, blank=True)

    def validate(self, data):
        self.REQUIREMENTS.validate(data)  # handle validation error

您可以将REQUIREMENTS 放在您的模型上,并让您的 DRF 和 Django 表单使用它来验证您的数据。

Here 是一篇解释更多的博文

【讨论】:

以上是关于根据另一个字段的值验证 Django 模型字段?的主要内容,如果未能解决你的问题,请参考以下文章

根据模型的另一个字段的最大值返回模型的值

在 Django 中,根据模型中其他字段中选择的值删除选择字段下拉列表中的选项

Django:如何根据来自行的数据和来自另一个模型的数据将聚合字段添加到查询集中?

根据另一个字段中的选择显示/隐藏 django 管理表单字段

在 Django 中模拟模型字段验证器

Django Crispy 表单根据需要设置模型字段