Django - UpdateView与内联formset试图保存重复记录?

Gar*_*nzo 14 django django-forms django-class-based-views

我有一个Expense模型和一个ExpenseLineItem模型.就像典型的费用/发票一样,一个费用可以有多个行项目来弥补发票的总成本.我正在尝试使用基于类的视图来创建和更新费用.我已成功编码CreateView以使用多个费用订单项进行新费用.

我的问题是当我尝试更新已有多个费用行项目的现有费用时.这是我下面的代码,我无法弄清楚问题是什么.该混入(TitleMixin,CancelSuccessMixin,SelectedApartment)是我的,做工精细.

我收到一个错误,我相信,这意味着它正在尝试保存副本,ExpenseLineItems但由于那些已经存在而失败.几乎就像我没有提出instance论点.

我究竟做错了什么?

forms.py

class ExpenseForm(ModelForm):
  class Meta:
    model = Expense
    fields = ['apart', 'inv_num', 'vendor', 'due_date']

ExpenseLineItemFormset = inlineformset_factory(Expense, ExpenseLineItem, fields=('description', 'account', 'amt'), can_delete=False)
Run Code Online (Sandbox Code Playgroud)

这是我的ExpenseUpdate观点:

class ExpenseUpdate(TitleMixin, CancelSuccessMixin, SelectedApartment, UpdateView):
  model = Expense
  form_class = ExpenseForm
  template_name = 'accounting/expense.html'

  def get(self, request, *args, **kwargs):
    self.object = self.get_object()
    form_class = self.get_form_class()
    form = self.get_form(form_class)
    expense_line_item_form = ExpenseLineItemFormset(instance = self.object)
    return self.render_to_response(self.get_context_data(form = form, expense_line_item_form = expense_line_item_form))

  def post(self, request, *args, **kwargs):
    self.object = self.get_object()
    form_class = self.get_form_class()
    form = self.get_form(form_class)
    expense_line_item_form = ExpenseLineItemFormset(self.request.POST, instance=self.object)

    if (form.is_valid() and expense_line_item_form.is_valid()):
      return self.form_valid(form, expense_line_item_form)
    return self.form_invalid(form, expense_line_item_form)

  def form_valid(self, form, expense_line_item_form):
    self.object = form.save()
    expense_line_item_form.instance = self.object
    expense_line_item_form.save()
    return HttpResponseRedirect(self.get_success_url())

  def form_invalid(self, form, expense_line_item_form):
    return self.render_to_response(self.get_context_data(form=form, expense_line_item_form=expense_line_item_form))
Run Code Online (Sandbox Code Playgroud)

错误代码我得到:

MultiValueDictKeyError at /stuff/2/accounting/update-expense/25/
"u'expenselineitem_set-0-id'"
Request Method: POST
Request URL:    http://localhost:8000/stuff/2/accounting/update-expense/25/
Django Version: 1.8.3
Exception Type: MultiValueDictKeyError
Exception Value:    
"u'expenselineitem_set-0-id'"
Exception Location: /usr/local/lib/python2.7/dist-packages/django/utils/datastructures.py in __getitem__, line 322
Run Code Online (Sandbox Code Playgroud)

编辑:我的模板的相关部分:

<form class="form-horizontal" action="" method="post">
  {% csrf_token %}
  {% load widget_tweaks %}
 <div class="row">
    <div class="col-md-12">
      <table class="table table-tight">
        <thead>
          <th>Description</th>
          <th class="text-right">Account</th>
          <th class="text-right">Amount</th>
        </thead>
        <tbody>
          {{ expense_line_item_form.management_form }}
          {% for eli in expense_line_item_form %}
          <tr>
            <td>{{ eli.description|attr:'cols:29' }}</td>
            <td class="text-right">{{ eli.account }}</td>
            <td class="text-right">{{ eli.amt }}</td>
          </tr>
          {% endfor %}
        </tbody>
      </table>
    </div>
  <div class="col-md-12 text-right">
    <a href="{{ cancel }}" class="btn btn-default btn-lg">Cancel</a>
    <input class="btn btn-success btn-lg" type="submit" value="Post" />
  </div>
  <br><br>
</form>
Run Code Online (Sandbox Code Playgroud)

编辑 - 工作表单模板我以为我会添加我的模板的工作版本,如果其他人需要它:

    <tbody>
      {{ expense_line_item_form.management_form }}
      {% for eli in expense_line_item_form %}
      <tr>
        <td>{{ eli.id }} {{ eli.description|attr:'cols:29' }}</td> <!-- <<==== Here's where I simply added {{ eli.id }}. That's all I changed :) -->
        <td class="text-right">{{ eli.account }}</td>
        <td class="text-right">{{ eli.amt }}</td>
      </tr>
      {% endfor %}
    </tbody>
Run Code Online (Sandbox Code Playgroud)

Ala*_*air 9

您需要在formset中包含每个表单的表单ID(它不会显示给用户,因为它将呈现为隐藏输入).如果没有这种形式,POST数据中的值就会丢失,KeyError您可以看到正确的结果.

formset文档:

注意我们需要如何显式渲染{{ form.id }}.这可确保POST案例中的模型formset能够正常工作.(此示例假定名为id的主键.如果您已明确定义了自己的主键,而不是名为id,请确保它已呈现.)

在您的情况下,您循环使用formset {% for eli in expense_line_item_form %},因此您需要包含{{ eli.id }}.

  • 我不认为 `{{ form.id}}` 是一个新要求,所以我不知道为什么过去没有它可能会起作用。表单集内部结构非常复杂,所以我尽量避免在那里查看! (2认同)