django formset 未验证,因为需要 ID

Posted

技术标签:

【中文标题】django formset 未验证,因为需要 ID【英文标题】:django formset not validating because ID is required 【发布时间】:2018-08-08 03:10:09 【问题描述】:

我的视图从模板接收模型表单集,但它没有通过验证,声称需要 ID。到目前为止,我对表单的使用从未出现过这个问题,而且我也从未需要传递 ID。

这是我的观点的简化版本:

def BudgetView(request):

    import pdb
    pdb.set_trace()

    if request.user.is_authenticated:
        U=request.user

        #initalize formset factories
        ItemFormSet = modelformset_factory(Item, fields=(blabla), extra=0)
        CatFormset=modelformset_factory(BudgetCatagory, fields=(blabla), extra=0)

        #initalize Constants
        InitiateConstants(CatagoryItemsList)

        if request.method=='POST':
            FormsetItem=ItemFormSet(request.POST,initial=Item.objects.filter(budgetcatagory__user_id=U.id).values())
            FormsetCat=CatFormset(request.POST)
            if FormsetItem.is_valid():
-bla
-bla
-bla

            return redirect('/HighLevelInput')
        else:
            #populate
            I=Item.objects.filter(budgetcatagory__user_id=U.id)
            C=BudgetCatagory.objects.filter(user_id=U.id)

            #initiate initial catagories and items for new user
            if (not I.exists()) or (not C.exists()):
                Item.objects.filter(budgetcatagory__user_id=U.id).delete()
                BudgetCatagory.objects.filter(user_id=U.id).delete()
                InitiateNewUser(U)
                I=Item.objects.filter(budgetcatagory__user_id=U.id)
                C=BudgetCatagory.objects.filter(user_id=U.id)
            FormsetItem=ItemFormSet(queryset=I)
            FormsetCat=CatFormset(queryset=C)

        return render(request,'getdata/budgetmachine.html', 'FormsetItem':FormsetItem, 'FormsetCat':FormsetCat )
    else:
        return redirect('/login')

is_valid 返回False,原因我上面已经提到了。 有什么想法吗?

根据要求,以下是is_valid 检查返回的错误:

(Pdb) FormsetItem.errors
['id': ['This field is required.'], 'id': ['This field is required.'], 'id': ['This field is required.'], 'id': ['This field is required.'], 'name': ['This field is required.'], 'detail': ['This field is required.'], 'layout': ['This field is required.'], 'unit': ['This field is required.'], 'unit_description': ['This field is required.'], 'parent': ['This field is required.'], 'enName': ['This field is required.'], 'id': ['This field is required.'], 'id': ['This field is required.'], 'id': ['This field is required.'], 'id': ['This field is required.'], 'id': ['This field is required.'], 'id': ['This field is required.'], 'id': ['This field is required.'], 'id': ['This field is required.'], 'id': ['This field is required.'], 'id': ['This field is required.'], 'id': ['This field is required.'], 'name': ['Ensure this value has at most 30 characters (it has 32).'], 'parent': ['Ensure this value has at most 30 characters (it has 32).'], 'id': ['This field is required.'], 'id': ['This field is required.'], 'id': ['This field is required.'], 'id': ['This field is required.'], 'id': ['This field is required.'], 'id': ['This field is required.'], 'id': ['This field is required.'], 'id': ['This field is required.'], 'id': ['This field is required.'], 'id': ['This field is required.'], 'id': ['This field is required.']]

这是我的模板:

% block body %

<div class="container" style="width:80%">

  <form method="post">
    % csrf_token %
     FormsetItem.management_form 
     FormsetCat.management_form 

    <table>
      <tr>
        <th>פריט</th>
        <th>מחיר מתוקצב</th>
        <th>מיקום מחיר</th>
        <th>רמת פירוט</th>
        <th>רמת פירוט</th>
      </tr>

      <!--unpacks the item dictionary into formsets -->
      % for CatForm in FormsetCat %
        <tbody onmouseenter="ToggleDisable(' CatForm.enName.value')" onmouseleave="ToggleDisable(' CatForm.enName.value')">
          % for ItemForm in FormsetItem %
            % if ItemForm.parent.value == CatForm.name.value %
                % if ItemForm.layout.value == 'normal' %
                  % include 'getdata/normalBudgetLayout.html' with form=ItemForm itemCount=forloop.counter0 catagoryCount=forloop.parentloop.counter0 %
                % elif ItemForm.layout.value == 'choice' %
                  % include 'getdata/choiceBudgetLayout.html' with form=ItemForm itemCount=forloop.counter0 catagoryCount=forloop.parentloop.counter0 %
                % endif %
            % endif %
          % endfor %
        <tr class="txt_center Row_ CatForm.enName.value" style="line-height:4em; background:Silver;">
          <td>
            <a onclick="ToggleDisable(' CatForm.enName.value')">
               CatForm.name.value 
              <span style="float:left;">
                <i class="fa fa-caret-down  CatForm.enName.value" style="font-size:30px; padding:7px;"></i>
                <i class="fa fa-caret-up  CatForm.enName.value" style="font-size:30px; padding:7px;" hidden></i>
              </span>
            <!--/a-->
          </td>
          <td> CatForm.catagory_cost </td>
          <td>TBD</td>
          <td> CatForm.detail.value </td>
          <td></td>
        </tr>
        </tbody>
      % endfor %
    </table>
    <br />
    <br />
    <br />
    <button type="submit" >Submit</button>    <br />
    <br />

  </form>
</div>




% endblock %

谢谢

【问题讨论】:

请显示确切的错误。请注意,在 POST 块中传递 initial 根本没有任何作用。为什么不像在 else 块中那样传递 queryset 呢? 新的,所以我每天都在学习新东西......我想使用一个查询集,但我读到初始只需要一个字典列表,而查询集需要一个查询集是对象。另外,我读到使用 initial 启动请求中的数据之间的比较,并且只更新已更改或添加的内容....将尝试您的观点,谢谢。但是问题仍然存在......它没有给我一个错误,只是 is_valid 返回 false 并且错误字段显示 id 是必需的。 您仍然需要显示该错误字段包含的内容。你也应该展示你的模板。但是我对您关于初始/查询集的观点感到非常困惑。在非 POST 情况下,您应该始终传递一个查询集,并且只传递初始值(如果您需要它)。 你的意思是这样的:FormsetItem=ItemFormSet(request.POST, queryset=Item.objects.filter(budgetcatagory__user_id=U.id)) ?? 【参考方案1】:

在这种情况下,错误消息非常清楚:您必须呈现“id”字段,否则 POST 将不包含每条记录的主键值。

我建议使用crispy forms 并让它呈现整个表单集或手动呈现模板中的字段。 在第一种情况下,你会有这样的东西:

% load crispy_forms_tags %
<form action="post" ...>
    % crispy formset %
</form>

否则:

<form action="post" ...> 
    <table>
        <tbody>
           % for form in formset %
           <tr>
              <td> form.field1 </td>
              <td> form.field2 </td>
              <td class="hidden"> form.id </td>
           </tr>
        </tbody>
    </table>             
</form>

【讨论】:

【参考方案2】:

ModelFormsets 需要 form.id。它呈现为隐藏字段。您将需要使用这两个表单集来实现它。

% for form in formset %
    % for hidden in form.hidden_fields %
         hidden 
    % endfor %
    <!-- form.visible fields go here -->
% endfor %

【讨论】:

另外,好像使用management_form会出现这个问题。因为management_form表示你会管理所有的,所以隐藏字段也需要管理。 另见@j-a-n-u-s answer below【参考方案3】:

作为@unixo 答案的补充,简单地说:

 form.id 

没有任何周围的 HTML 标记,将在呈现模板时转换为以下内容(namevalueid 的值将由您的 formset_factory 生成):

<input type="hidden" name="form-1-id" value="2" id="id_form-1-id">

只要确保它缩进到 for form in formset 循环中即可。

这意味着您不需要添加class="hidden",除非您对隐藏字段有特定的处理方式。

【讨论】:

此解决方案适用于标准解决方案(循环通过隐藏字段)没有的地方。惊人的!它为我解决了一个非常棘手的问题。

以上是关于django formset 未验证,因为需要 ID的主要内容,如果未能解决你的问题,请参考以下文章

需要在 Django Formset 中有一个必填字段和可选字段

如何在 Django 中根据需要为 formset_factory 设置每个字段(如何验证表单集中的空白表单)

Django之路——form modelform formset modelformset的各种用法

Django formset 不验证

Django Formset管理-表单验证错误

Tutorial : Implementing Django Formsets