Django - 带有试图保存重复记录的内联表单集的 UpdateView?

Django - UpdateView with inline formsets trying to save duplicate records?

我有一个 Expense 模型和一个 ExpenseLineItem 模型。就像典型的 expense/invoice 一样,一笔费用可以有多个行项目来构成一张发票的总成本。我正在尝试使用基于 class 的视图来创建和更新费用。我已经成功地编写了 CreateView 代码,以使用多个费用明细项目来产生新的费用。

我的问题是当我尝试更新已有多个费用明细项目的现有费用时。下面是我的代码,我不知道问题出在哪里。 mixin(TitleMixinCancelSuccessMixinSelectedApartment)是我的并且工作正常。

我收到一个错误,我认为这意味着它正在尝试保存 ExpenseLineItemsnew 副本,但失败了,因为它们已经存在。几乎就像我没有提供 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)

这是我的 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))

我得到的错误代码:

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

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

<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>

编辑——工作表单模板我想我会添加我的模板的工作版本,如果其他人需要的话:

    <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>

您需要在表单集中包含每个表单的表单 ID(它不会向用户显示,因为它呈现为隐藏输入)。如果没有该表格,POST 数据中将缺少该值,您会得到一个 KeyError,如您所见。

来自formset docs

Notice how we need to explicitly render {{ form.id }}. This ensures that the model formset, in the POST case, will work correctly. (This example assumes a primary key named id. If you’ve explicitly defined your own primary key that isn’t called id, make sure it gets rendered.)

在您的例子中,您正在使用 {% for eli in expense_line_item_form %} 遍历表单集,因此您需要包含 {{ eli.id }}.