Django のクラスベース汎用ビューの CreateView や UpdateView で、下のような FormSet を使いたくなりました。
from django.forms import ModelForm from django.forms.models import inlineformset_factory from receipts.models import Receipt, Detail class ReceiptForm(ModelForm): class Meta: model = Receipt exclude = ("user", "created") DetailFormSet = inlineformset_factory(Receipt, Detail)
しか〜し!CreateView や UpdateView は FormSet には対応していません。そこで、get_context_data と form_valid オーバーライドして、無理やり FormSet を使ってみました。下は CreateView の例。
from django.core.urlresolvers import reverse from django.shortcuts import redirect from django.views.generic import CreateView from receipts.models import Receipt, Detail from receipts.forms import ReceiptForm, DetailFormSet class CreateReceiptView(CreateView): template_name = "receipts/create_receipt.html" form_clas = ReceiptForm def get_success_url(self): return reverse("receipt_detail", kwargs={"pk": self.object.id}) def get_context_data(self, **kwargs): ctx = super(CreateReceiptView, self).get_context_data(**kwargs) if self.request.method == "POST": ctx["formset"] = DetailFormSet(self.request.POST, self.request.FILES) else: ctx["formset"] = DetailFormSet() return ctx def form_valid(self, form): ctx = self.get_context_data() formset = ctx["formset"] if formset.is_valid(): self.object = form.save(commit=False) self.object.user = self.request.user self.object.save() # FormSet の内容を保存 formset.instance = self.object formset.save() return redirect(self.get_redirect_url()) else: ctx["form"] = form return self.render_to_response(ctx)
Django のソースコードを見ながら修正したわけじゃないので、テンプレートに渡すコンテキストに詰めるデータが足りないかも…。今回はこれで一応動きましたが。