Django - 在基于类的视图中添加行到formset没有javascript

时间:2014-07-16 05:45:05

标签: django django-forms django-views django-class-based-views

我正在尝试使用内联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

1 个答案:

答案 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窗体。