我正在尝试使用内联formset创建一个基于类的表单视图。我想要一个'添加'将向formset添加新行的按钮,我希望它在没有javascript的情况下工作。
我找到了一些有用的网站,这些网站允许我创建一些粗略的代码(如下) https://stackoverflow.com/a/11910420/3800244:提供一种优雅/干燥方法,用于将表单集添加到CBV http://pytipz.blogspot.com.au/2012/09/django-adding-inline-formset-rows.html:显示如何在视图中向表单集添加行,但它似乎是基于函数的视图。
我的用例是纸牌游戏的网站。我有一张牌桌,一张甲板桌和一张卡片桌。卡片表中填充了数据,当人们创建卡片时,他们需要能够为该卡片选择卡片(所有卡片都在同一页面/表格上)。这是我到目前为止的一些粗略代码;
models.py
class Card(models.Model):
title = models.CharField()
class Deck(models.Model):
title = models.CharField()
class CardsInDeck(models.Model):
deck = models.ForeignKey(Deck)
card = models.ForeignKey(Card)
quantity = models.PositiveSmallIntegerField(blank=False, default=1)
forms.py
class DeckForm(forms.ModelForm):
class Meta:
model = Deck
CardsInDeckFormSet = inlineformset_factory(Deck, CardsInDeck, extra=1)
views.py
class Deck_FormView(generic.edit.FormView):
template_name = 'create_deck.html'
model = Deck
form_class = DeckForm
success_url = 'viewdeck' #just an example
def get_formsets(self):
return {
'cards': CardsInDeckFormSet(self.request.POST or None, prefix='cards'),
}
def get_context_data(self, **kwargs):
context = super(Deck_FormView, self).get_context_data(**kwargs)
context['formsets'] = self.get_formsets()
return context
def post(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
if 'add_card' in request.POST:
request.POST = request.POST.copy()
request.POST['cards-TOTAL_FORMS'] = int(request.POST['cards-TOTAL_FORMS']) + 1
return self.render_to_response(self.get_context_data(form=form))
formsets = self.get_formsets()
if (form.is_valid() and all((f.is_valid() for f in formsets.values()))):
return self.form_valid(form, formsets)
else:
return self.form_invalid(form)
def form_valid(self, form, formsets):
self.object = form.save()
for name, formset in formsets.items():
formset_save_func = getattr(self, 'formset_{0}_valid'.format(name), None)
if formset_save_func is not None:
formset_save_func(formset)
else:
formset.save()
return HttpResponseRedirect(self.get_success_url())
def formset_cards_valid(self, formset):
cards_formsets = formset.save(commit=False)
for i in cards_formsets:
i.deck = self.object
i.save()
create_deck.html
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<fieldset>
<legend>Cards in deck</legend>
{{ formsets.cards.management_form }}
{% for form in formsets.cards %}
{{ form.id }}
<div class="inline {{ formsets.cards.prefix }}">
{{ form }}
</div>
{% endfor %}
<input type='submit' name='add_card' value="Add another card" />
</fieldset>
<br>
<input type='hidden' name='action' value="deck">
<input type='submit' name='submit' value="Save Deck" />
</form>
所以我的代码使用了我最初引用的方法。它显示表单和表单集,然后单击&#34;添加&#34;按钮使用额外的formset行重新加载页面。节约也有效 问题;创建新行时没有任何&#39;默认值&#39; (即卡片选择输入中未选择任何内容,并且未在数字输入中输入默认数量1)。此外,新的&#39;空&#39;行通过验证,HTML呈现&#34;此字段是必需的。&#34;每个领域的错误。
我想解决这些问题,使其对用户更有用。我不知道该怎么做。我想知道是否可能与设置is_bound到表单/ formset有关?我也相信以下链接可能是相关的,但我无法确定如何实现它(如果它确实是解决方案):Django class based views and formsets
答案 0 :(得分:3)
通过'手动'设置新formset表单的每个字段的属性,我找到了一个解决方案。以下是经过修改的def post
。
def post(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
formsets = self.get_formsets()
if 'add_card' in request.POST:
request.POST = request.POST.copy()
request.POST['cards-TOTAL_FORMS'] = int(request.POST['cards-TOTAL_FORMS']) + 1
# This is the new block of code that loops over each base field,
# and uses the initial values provided.
new_formset_id = request.POST['cards-TOTAL_FORMS'] - 1
for field in formsets['cards'].empty_form.base_fields:
request.POST['cards-{0}-{1}'.format(new_formset_id, field)] = formsets['cards'].empty_form.base_fields[field].initial
return self.render_to_response(self.get_context_data(form=form))
if (form.is_valid() and all((f.is_valid() for f in formsets.values()))):
return self.form_valid(form, formsets)
else:
return self.form_invalid(form)
添加表单时不会出现验证错误。保存主窗体后,将忽略这些空/默认formset窗体。