在Django中在一个页面上处理多个表单的正确方法

Ada*_*son 175 python forms django

我有一个期待两种形式的模板页面.如果我只使用一个表单,那么就像这个典型的例子一样好:

if request.method == 'POST':
    form = AuthorForm(request.POST,)
    if form.is_valid():
        form.save()
        # do something.
else:
    form = AuthorForm()
Run Code Online (Sandbox Code Playgroud)

但是,如果我想使用多个表单,我如何让视图知道我只提交其中一个表单而不是另一个表单(即它仍然是request.POST但我只想处理提交的表单事情)吗?


这是该解决方案基于这样的答案expectedphrasebannedphrase是不同形式和不同的提交按钮的名称expectedphraseformbannedphraseform是形式.

if request.method == 'POST':
    if 'bannedphrase' in request.POST:
        bannedphraseform = BannedPhraseForm(request.POST, prefix='banned')
        if bannedphraseform.is_valid():
            bannedphraseform.save()
        expectedphraseform = ExpectedPhraseForm(prefix='expected')
    elif 'expectedphrase' in request.POST:
        expectedphraseform = ExpectedPhraseForm(request.POST, prefix='expected')
        if expectedphraseform.is_valid():
            expectedphraseform.save() 
        bannedphraseform = BannedPhraseForm(prefix='banned')
else:
    bannedphraseform = BannedPhraseForm(prefix='banned')
    expectedphraseform = ExpectedPhraseForm(prefix='expected')
Run Code Online (Sandbox Code Playgroud)

Ned*_*der 124

你有几个选择:

  1. 在两个表单的操作中放入不同的URL.然后你将有两个不同的视图函数来处理两种不同的形式.

  2. 从POST数据中读取提交按钮值.您可以判断单击了哪个提交按钮:如何构建多个提交按钮django表单?

  • 4)添加一个标识表单的隐藏字段,并在视图中检查该字段的值. (12认同)
  • 3)确定从POST数据中的字段名称提交的表单.如果您的froms没有唯一字段且所有可能的值都不为空,请包含一些隐藏的输入. (5认同)
  • #1真的是你最好的选择.您不希望使用隐藏字段污染POST,也不想将视图绑定到模板和/或表单. (5认同)
  • @meteorainer如果您使用第一,是否有办法将错误传递回父视图中实例化这些错误的表单,而不使用消息框架或查询字符串?这个答案似乎是最接近的,但在这里它仍然只是一种处理两种形式的视图:http://stackoverflow.com/a/21271659/2532070 (3认同)

Ada*_*son 38

未来参考的方法是这样的.bannedphraseform是第一种形式,而expectphraseform是第二种形式.如果第一个被击中,则跳过第二个(在这种情况下这是一个合理的假设):

if request.method == 'POST':
    bannedphraseform = BannedPhraseForm(request.POST, prefix='banned')
    if bannedphraseform.is_valid():
        bannedphraseform.save()
else:
    bannedphraseform = BannedPhraseForm(prefix='banned')

if request.method == 'POST' and not bannedphraseform.is_valid():
    expectedphraseform = ExpectedPhraseForm(request.POST, prefix='expected')
    bannedphraseform = BannedPhraseForm(prefix='banned')
    if expectedphraseform.is_valid():
        expectedphraseform.save()

else:
    expectedphraseform = ExpectedPhraseForm(prefix='expected')
Run Code Online (Sandbox Code Playgroud)

  • 使用prefix =确实是'正确的方法' (5认同)

Dan*_*ski 13

Django基于类的视图提供了一个通用的FormView,但是出于所有意图和目的,它只能处理一个表单.

使用Django的通用视图处理具有相同目标操作URL的多个表单的一种方法是扩展'TemplateView',如下所示; 我经常使用这种方法,我已经把它变成了Eclipse IDE模板.

class NegotiationGroupMultifacetedView(TemplateView):
    ### TemplateResponseMixin
    template_name = 'offers/offer_detail.html'

    ### ContextMixin 
    def get_context_data(self, **kwargs):
        """ Adds extra content to our template """
        context = super(NegotiationGroupDetailView, self).get_context_data(**kwargs)

        ...

        context['negotiation_bid_form'] = NegotiationBidForm(
            prefix='NegotiationBidForm', 
            ...
            # Multiple 'submit' button paths should be handled in form's .save()/clean()
            data = self.request.POST if bool(set(['NegotiationBidForm-submit-counter-bid',
                                              'NegotiationBidForm-submit-approve-bid',
                                              'NegotiationBidForm-submit-decline-further-bids']).intersection(
                                                    self.request.POST)) else None,
            )
        context['offer_attachment_form'] = NegotiationAttachmentForm(
            prefix='NegotiationAttachment', 
            ...
            data = self.request.POST if 'NegotiationAttachment-submit' in self.request.POST else None,
            files = self.request.FILES if 'NegotiationAttachment-submit' in self.request.POST else None
            )
        context['offer_contact_form'] = NegotiationContactForm()
        return context

    ### NegotiationGroupDetailView 
    def post(self, request, *args, **kwargs):
        context = self.get_context_data(**kwargs)

        if context['negotiation_bid_form'].is_valid():
            instance = context['negotiation_bid_form'].save()
            messages.success(request, 'Your offer bid #{0} has been submitted.'.format(instance.pk))
        elif context['offer_attachment_form'].is_valid():
            instance = context['offer_attachment_form'].save()
            messages.success(request, 'Your offer attachment #{0} has been submitted.'.format(instance.pk))
                # advise of any errors

        else 
            messages.error('Error(s) encountered during form processing, please review below and re-submit')

        return self.render_to_response(context)
Run Code Online (Sandbox Code Playgroud)

html模板具有以下效果:

...

<form id='offer_negotiation_form' class="content-form" action='./' enctype="multipart/form-data" method="post" accept-charset="utf-8">
    {% csrf_token %}
    {{ negotiation_bid_form.as_p }}
    ...
    <input type="submit" name="{{ negotiation_bid_form.prefix }}-submit-counter-bid" 
    title="Submit a counter bid"
    value="Counter Bid" />
</form>

...

<form id='offer-attachment-form' class="content-form" action='./' enctype="multipart/form-data" method="post" accept-charset="utf-8">
    {% csrf_token %}
    {{ offer_attachment_form.as_p }}

    <input name="{{ offer_attachment_form.prefix }}-submit" type="submit" value="Submit" />
</form>

...
Run Code Online (Sandbox Code Playgroud)


ybe*_*ana 11

我需要在同一页面上独立验证的多个表单.我遗漏的关键概念是1)使用提交按钮名称的表单前缀和2)无界形式不会触发验证.如果它对其他人有帮助,这里是我使用TemplateView的两种形式AForm和BForm的简化示例,基于@ adam-nelson和@ daniel-sokolowski的答案以及@zeraien的评论(/sf/answers/1211243631/):

# views.py
def _get_form(request, formcls, prefix):
    data = request.POST if prefix in request.POST else None
    return formcls(data, prefix=prefix)

class MyView(TemplateView):
    template_name = 'mytemplate.html'

    def get(self, request, *args, **kwargs):
        return self.render_to_response({'aform': AForm(prefix='aform_pre'), 'bform': BForm(prefix='bform_pre')})

    def post(self, request, *args, **kwargs):
        aform = _get_form(request, AForm, 'aform_pre')
        bform = _get_form(request, BForm, 'bform_pre')
        if aform.is_bound and aform.is_valid():
            # Process aform and render response
        elif bform.is_bound and bform.is_valid():
            # Process bform and render response
        return self.render_to_response({'aform': aform, 'bform': bform})

# mytemplate.html
<form action="" method="post">
    {% csrf_token %}
    {{ aform.as_p }}
    <input type="submit" name="{{aform.prefix}}" value="Submit" />
    {{ bform.as_p }}
    <input type="submit" name="{{bform.prefix}}" value="Submit" />
</form>
Run Code Online (Sandbox Code Playgroud)


cha*_*uur 11

想分享我没有使用 Django Forms 的解决方案。我在一个页面上有多个表单元素,我想使用一个视图来管理来自所有表单的所有 POST 请求。

我所做的是引入了一个不可见的输入标签,以便我可以将参数传递给视图以检查已提交的表单。

<form method="post" id="formOne">
    {% csrf_token %}
   <input type="hidden" name="form_type" value="formOne">

    .....
</form>

.....

<form method="post" id="formTwo">
    {% csrf_token %}
    <input type="hidden" name="form_type" value="formTwo">
   ....
</form>
Run Code Online (Sandbox Code Playgroud)

视图.py

def handlemultipleforms(request, template="handle/multiple_forms.html"):
    """
    Handle Multiple <form></form> elements
    """
    if request.method == 'POST':
        if request.POST.get("form_type") == 'formOne':
            #Handle Elements from first Form
        elif request.POST.get("form_type") == 'formTwo':
            #Handle Elements from second Form
Run Code Online (Sandbox Code Playgroud)

  • 虽然这可能不是最优雅和可扩展的解决方案,但它绝对非常简单且易于实现。与其他所有产品相比,这是迄今为止最快的。我已经在自定义 CBV 中实现了这一点,并且效果非常好。好主意! (2认同)