我该如何在Django表单中构建多个提交按钮?

200

我有一个表单,其中有一个用于输入电子邮件的输入框,还有两个提交按钮,分别用于订阅和取消订阅电子报:

<form action="" method="post">
{{ form_newsletter }}
<input type="submit" name="newsletter_sub" value="Subscribe" />
<input type="submit" name="newsletter_unsub" value="Unsubscribe" />
</form>

我也有一个名为 form 的类:

class NewsletterForm(forms.ModelForm):
    class Meta:
        model = Newsletter
        fields = ('email',)

我必须编写自己的clean_email方法,我需要知道是哪个按钮提交了表单。但是提交按钮的值不在self.cleaned_data字典中。 是否可以以其他方式获取按钮的值?

6个回答

289
Eg:
if 'newsletter_sub' in request.POST:
    # do subscribe
elif 'newsletter_unsub' in request.POST:
    # do unsubscribe

1
在 clean_xxx 验证方法中是否可用 request? - sureshvv
这应该用什么方法实现? - Jon McClung
5
在“def post(self, request, *args, **kwargs)”函数内部。 - A. Kali
如果您想要使用身份验证的管理员本地化,请覆盖 ModelAdmin._changeform_view - Sy Ker
感谢您,来自13年前的Damon!!! 我花了好几个小时在这个上面努力。这个方法如此简洁而优雅。 - BLimitless

116
你可以在 clean_email 方法中使用 self.data 来访问验证之前的 POST 数据。它应该包含一个名为 newsletter_subnewsletter_unsub 的键,具体取决于哪个按钮被按下。
# in the context of a django.forms form

def clean(self):
    if 'newsletter_sub' in self.data:
        # do subscribe
    elif 'newsletter_unsub' in self.data:
        # do unsubscribe

4
你能给一个带注释的例子吗?这会非常有帮助。 - Jharwood
11
我认为clean并不是进行模型级别逻辑的合适场所。它专门用于清理表单并查找跨多个输入的任何ValidationError。Sven的答案更接近正确方向,但仍然不一定是最好的答案。 - Patrick
11
安全风险!在表单通过验证之前使用表单数据来更改数据库是危险的。请注意确保先验证表单再进行相关操作。 - Quant Metropolis
1
您可能希望根据按下的按钮运行不同类型的验证。在这种情况下,没有“安全”影响。 - sureshvv
你会如何使用 TestCase.client.post 进行测试? - germannp

38

你也可以这样做,

 <form method='POST'>
    {{form1.as_p}}
    <button type="submit" name="btnform1">Save Changes</button>
    </form>
    <form method='POST'>
    {{form2.as_p}}
    <button type="submit" name="btnform2">Save Changes</button>
    </form>

代码

if request.method=='POST' and 'btnform1' in request.POST:
    do something...
if request.method=='POST' and 'btnform2' in request.POST:
    do something...

form1.as_p 什么时候使用?在视图中检查表单的有效性时,我希望能够这样做... form1.is_valid() 然后执行此操作,form2.is_valid() 执行此操作... - hlkstuv_23900

8

一个URL链接到同一个页面!就像这样!

urls.py

url(r'^$', views.landing.as_view(), name = 'landing'),

views.py

class landing(View):
        template_name = '/home.html'
        form_class1 = forms.pynamehere1
        form_class2 = forms.pynamehere2
            def get(self, request):
                form1 = self.form_class1(None)
                form2 = self.form_class2(None)
                return render(request, self.template_name, { 'register':form1, 'login':form2,})

             def post(self, request):
                 if request.method=='POST' and 'htmlsubmitbutton1' in request.POST:
                        ## do what ever you want to do for first function ####
                 if request.method=='POST' and 'htmlsubmitbutton2' in request.POST:
                         ## do what ever you want to do for second function ####
                        ## return def post###  
                 return render(request, self.template_name, {'form':form,})

/home.html
    <!-- #### form 1 #### -->
    <form action="" method="POST" >
      {% csrf_token %}
      {{ register.as_p }}
    <button type="submit" name="htmlsubmitbutton1">Login</button>
    </form>
    <!--#### form 2 #### -->
    <form action="" method="POST" >
      {% csrf_token %}
      {{ login.as_p }}
    <button type="submit" name="htmlsubmitbutton2">Login</button>
    </form>

我应该如何从其他HTML文件中引用特定视图 href = "{% url 'appname:viewname' %}" - uma_8331
在views.py中,我应该为表单提供什么? - uma_8331
你为什么要在post函数中测试方法是否为POST?基于类的视图只有在HTTP方法为POST时才调用post函数。 - Braiam
我似乎记不起来为什么了,因为这是4年前的事情。然而,我似乎记得这是必要的,因为一个表单被提交而另一个表单没有,导致了循环操作方法。虽然不是完全确定。 - chrisroker0

5

虽然这是一个老问题,但我也遇到了同样的问题,并找到了适合我的解决方案:我编写了MultiRedirectMixin。

from django.http import HttpResponseRedirect

class MultiRedirectMixin(object):
    """
    A mixin that supports submit-specific success redirection.
     Either specify one success_url, or provide dict with names of 
     submit actions given in template as keys
     Example: 
       In template:
         <input type="submit" name="create_new" value="Create"/>
         <input type="submit" name="delete" value="Delete"/>
       View:
         MyMultiSubmitView(MultiRedirectMixin, forms.FormView):
             success_urls = {"create_new": reverse_lazy('create'),
                               "delete": reverse_lazy('delete')}
    """
    success_urls = {}  

    def form_valid(self, form):
        """ Form is valid: Pick the url and redirect.
        """

        for name in self.success_urls:
            if name in form.data:
                self.success_url = self.success_urls[name]
                break

        return HttpResponseRedirect(self.get_success_url())

    def get_success_url(self):
        """
        Returns the supplied success URL.
        """
        if self.success_url:
            # Forcing possible reverse_lazy evaluation
            url = force_text(self.success_url)
        else:
            raise ImproperlyConfigured(
                _("No URL to redirect to. Provide a success_url."))
        return url

2
您将在哪里以及如何使用MultiRedirectMixin?请举例详细说明。 - Saurav Kumar

2
我知道这是老的问题,但其中一些答案过于简略,而且它们没有涉及到一个常见的情况,即表单不是Django表单。
这个解决方案受到了这篇博客文章的启发。它依赖于一个从django.views.generic.edit.FormMixin派生出来的视图类,例如CreateView、UpdateView或DeleteView。这些类提供了get_success_url方法,其中在request中公开了按钮名称。 html
<html>
    <body>
        <form method="post">
            <div>
                <label> <input type="radio" name="select-type" value="A">Type A</label>
            </div>
            <div>
                <label> <input type="radio" name="select-type" value="B">Type B</label>
            </div>
            <div>
                <input type="submit" value="Use selected">
            </div>
            <div>
                <input type="submit" name="no-selection" value="None of the above">
            </div>
        </form>
    </body>
</html>

views.py

from django.views.generic import UpdateView

class GetType(UpdateView):
    def get(self, request):
        return render(request, 'get_type.html', {})

    def post(self, request):
        button = self.get_success_url()
        print(button)

    def get_success_url(self):
        if 'no-selection' in self.request.POST:
            return 'none selected'
        return ''

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接