2015-09-14 9 views
8

私はExpenseモデルとExpenseLineItemモデルを持っています。典型的な経費/請求書と同様に、請求書の総費用を補うために、1つの費用に複数の明細を含めることができます。私はクラスベースのビューを使用して費用を作成し、更新しようとしています。私はCreateViewを複数の経費明細で新しい経費を作るようにうまくコーディングしました。Django - 重複したレコードを保存しようとしているインラインformsetsのUpdateView?

私の問題は、すでに複数の経費明細がある既存の経費を試して更新しようとするときです。私のコードは以下の通りですが、問題の内容を把握することはできません。ミックスイン(TitleMixinCancelSuccessMixinSelectedApartment)はうまく動作します。

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) 

ここに私の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> 

EDIT - ワーキングフォームテンプレート私は他の誰かがそれを必要とすべきである私は、私のテンプレートの作業バージョンを追加するだろうと思った:

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

テンプレートを表示してください。 formsetの各フォームに '{{form.id}} 'を含めていることを確認してください。 – Alasdair

+0

@Alasdairテンプレートを追加しました。しかし、私は '{{form.id}} 'を含むことが、フォームセットの仕組みに基づいて必要であるとは確信していません。過去に書式セットを使用しましたが、既存のレコードセットを更新するときにPKを含める必要はありませんでした。しかし確かに私はここで間違っているかもしれません。 – Garfonzo

+0

私はあなたがフォームIDを必要とすると確信しています。下の私の答えを見てください。 – Alasdair

答えて

4

あなたは、各フォームのフォームIDを含める必要がフォームセット内に表示されます(隠し入力として表示されるため、ユーザーには表示されません)。そのフォームがなければ、値はPOSTデータにはないので、表示されているとおりにKeyErrorが得られます。

formset docsから:

我々は明示的{{ form.id }}をレンダリングする必要があるかに注意してください。これにより、モデルのformsetがPOSTケースで正しく動作することが保証されます。あなたのケースでは(この例では、IDという名前の主キーを前提としています。あなたが明示的にidというされていない独自の主キーを定義した場合、それがレンダリングされることを確認してください。)

、あなたはフォームセットをループしています{% for eli in expense_line_item_form %}であるため、{{ eli.id }}を含める必要があります。

+0

素晴らしいです、あなたは正しいです!ありがとうございました:)以前はフォームセットを使用していましたが、 '{{form.id}} 'フィールドは必要ありませんでしたが、おそらくこれは古いバージョンのDJangoのためでした。 – Garfonzo

+1

私は '{{form.id}}'が新しい要件だとは思わないので、過去にそれがなくてもなぜ機能しなかったのか分かりません。 formsetの内部構造はかなり複雑ですので、私ができるときはそこを調べることは避けてください! – Alasdair

関連する問題