【问题标题】:Why does known valid Django model instance fail is_valid() after retrieval from database?为什么从数据库检索后已知有效的 Django 模型实例失败 is_valid()?
【发布时间】:2011-01-23 13:17:48
【问题描述】:

我们有一个 Django 模型 ToolDataset 和一个 ModelForm ToolForm。在模型中,每个实例或数据库行都称为数据集,主键称为 dataset_id。第一次通过时,用户填写一个未绑定的表单并提交。从概念上讲,这是用于验证、保存和分析数据集的视图代码:

if (request.method == 'POST') and (not dataset_id):
    form = ToolForm(request.POST)
    if form.is_valid():
        new_dataset = form.save()
        dataset_id = new_dataset.pk
        results = analyze(form.cleaned_data)
    else:
        <validation error code>

我认为到目前为止这很正常。请注意,除非数据有效,否则不会保存表单数据,也不会分配 dataset_id。

现在一段时间过去了,用户想要返回到这个特定的旧数据集,也许是为了更改数据并重新分析它。因此,无论采用何种方式,都会组成一个类似于 www.finesite.com/Tool/X/ 的 URL,其中 X 是对应于用户想要使用的特定数据行的 dataset_id。通过 URLconf,视图代码的不同分支被调用,我们认为应该如下所示:

if (request.method != 'POST') and (dataset_id):
    oldset = get_object_or_404(ToolDataset, pk=dataset_id)
    form = ToolForm(instance=oldset)
    if form.is_valid():
        results = analyze(form.cleaned_data)
    else:
        <validation error code that we expected would never run>

好吧,事实证明,这个数据集在我们存储它时是有效的,但现在没有验证,这令人惊讶。我们使用 manage.py shell 来检查一下表单。以下是我们发现的一些内容:

>>> form.is_valid()
False
>>> form.errors
{}
>>> form.non_field_errors()
[]
>>> form.is_bound
False

运行 form.as_p() 会产生看似完整的表单。

一位非常有能力的同事在 django/forms/models.py 中发现了一个名为 model_to_dict() 的未记录 API 函数。他建议用这个代替,

form = BXEEP_L_Form(model_to_dict(oldset), instance=oldset),

为此,

form = BXEEP_L_Form(instance=oldset).

它现在可以工作了——根据 shell,表单是有效且绑定的——但我充满了疑问。为什么这行得通?为什么这是必要的?有没有更标准的方法来做到这一点?对于一个看起来如此普通和简单的用例,不得不使用一个未记录的内部函数似乎很奇怪。

【问题讨论】:

    标签: database django validation forms


    【解决方案1】:

    我们将分析函数的参数更改为模型实例,而不是 form.cleaned_data。这将分析与表单验证分离,并且更加明智。从概念上讲,上面给出的代码的第二部分现在看起来像这样:

    if (request.method != 'POST') and (dataset_id):
            oldset = get_object_or_404(ToolDataset, pk=dataset_id)
            form = ToolForm(instance=oldset)
            results = analyze(oldset)
    

    当然,在分析函数头部的数据解包必须重新编写。

    现在这一切似乎都很明显。谢谢大家的关注!

    【讨论】:

      【解决方案2】:

      form.is_valid() 验证 form.data 字典,该字典通过 Form(data=request.POST) 的构造函数发送

      ModelForm.instance 将数据与特定的表行相关联,因此保存必须执行更新而不是插入。这也是通过构造函数传递的。

      然而,这两者是相互独立的。如果您想使用旧实例的数据创建表单,您应该执行以下操作:

      ToolForm(data=oldinstance.__data__, instance=oldinstance)
      

      但是,您可能并不想立即绑定数据。

      ToolForm(instance=oldinstance)
      

      从实例中填充正确的值,当在 html 中呈现并更新记录时,仅当 ToolForm 实例 is_changed()

      【讨论】:

      • form = ToolForm(instance=oldset) 没有生成可以使用 is_valid() 检查的表单似乎很奇怪。不过,我接受你说的。我认为您的意思是 dict,而不是 data。将 dict 与模型实例一起使用的一些新复杂情况(请参阅此处:google.com/url?q=http://docs.djangoproject.com/en/dev/releases/…。)可能会使 model_to_dict 成为更好的选择。
      【解决方案3】:

      我可能误解了一些东西,但在我看来,您的 analyze 函数不应将 form.cleaned_data 作为输入,而应将 dataset_id 作为输入。

      如果示例不完整 - 为什么要从数据集创建表单来分析它?

      【讨论】:

      • 如果我使用 dataset_id 作为我的分析函数的参数,那将是一个更小的参数传递,但是分析函数需要进行数据库查询,我认为最终结果那会让事情放慢一点。真正控制整个过程的视图使用 get_object_or_404 部分获取此数据,以便将其呈现到页面。既然这个数据在手,为什么要查询数据库两次。为什么不直接把它传给analyze()呢?
      • 数据集包含工程设计问题的参数。当用户在一段时间后返回数据集时,可能是因为他想要优化设计(调整参数)或者只是查看旧结果。在任何一种情况下,都必须再次执行分析,以便他可以通过一系列计算的性能测量(称为统一检查)查看设计的当前状态,这些测量未保存,因此在重新计算之前无法显示分析函数。
      • 我现在明白你第一点的意思了。最好的办法是让分析函数的参数成为模型实例,而不是 form.cleaned_data。然后分析函数可以继续运行,而无需再次运行 is_valid()。 is_valid() 用于验证用户输入,因此这里没有必要使用它,因为之前已经完成了。除了稍微重写之外,此更改没有任何缺点。见下文。
      • 听起来不错——很高兴能帮上忙!
      猜你喜欢
      • 2020-11-16
      • 2020-03-14
      • 2016-05-14
      • 2012-06-19
      • 2021-02-17
      • 1970-01-01
      • 1970-01-01
      • 2011-10-13
      • 1970-01-01
      相关资源
      最近更新 更多