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

277 投票
13 回答
264099 浏览
提问于 2025-04-15 14:10

我有一个模板页面,里面需要两个表单。如果我只用一个表单,事情就很简单,像这个典型的例子:

if request.method == 'POST':
    form = AuthorForm(request.POST,)
    if form.is_valid():
        form.save()
        # do something.
else:
    form = AuthorForm()

但是如果我想同时处理多个表单,我该怎么让视图知道我只提交了其中一个表单,而不是其他的呢?(也就是说,虽然还是用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')

13 个回答

29

我需要在同一页面上有多个表单,而且这些表单要独立验证。之前我缺少的关键概念是:1)为提交按钮的名称使用表单前缀,2)一个没有限制的表单不会触发验证。如果这对其他人有帮助,这里是我简化的例子,包含两个表单 AForm 和 BForm,基于 @adam-nelson 和 @daniel-sokolowski 的回答,以及 @zeraien 的评论(https://stackoverflow.com/a/17303480/2680349):

# 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>
55

这里有一个方法可以供将来参考。bannedphraseform是第一个表单,expectedphraseform是第二个表单。如果第一个表单被触发了,那么第二个表单就会被跳过(在这种情况下,这个假设是合理的):

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')
198

你有几个选择:

  1. 为这两个表单设置不同的URL地址。这样你就可以为这两个不同的表单写两个不同的处理函数。

  2. 从POST数据中读取提交按钮的值。你可以知道是哪个提交按钮被点击了:如何在Django表单中创建多个提交按钮?

撰写回答