How can I build multiple submit buttons django form?
Asked Answered
C

6

201

I have form with one input for email and two submit buttons to subscribe and unsubscribe from newsletter:

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

I have also class form:

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

I must write my own clean_email method and I need to know by which button was form submited. But the value of submit buttons aren't in self.cleaned_data dictionary. Could I get values of buttons otherwise?

Caesarism answered 14/5, 2009 at 22:50 Comment(0)
P
116

You can use self.data in the clean_email method to access the POST data before validation. It should contain a key called newsletter_sub or newsletter_unsub depending on which button was pressed.

# 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
Prejudice answered 14/5, 2009 at 23:3 Comment(5)
can you give an annotated example? it'd really helpJemma
I don't think clean isn't really the right kind of place to do model-level logic. It's specifically for cleaning a form and finding any ValidationErrors that span multiple inputs. Sven's answer is more in the right direction, but still not necessarily the best answerRealgar
Security Risk! Using form data to make changes in the database before the form has been validated is dangerous.Team
You may want to run different types of validation depending on which button was pressed. In that case, there is no "Security" impact.Slumberland
How would you test this using TestCase.client.post?Hushhush
E
290

Eg:

if 'newsletter_sub' in request.POST:
    # do subscribe
elif 'newsletter_unsub' in request.POST:
    # do unsubscribe
Eleen answered 6/1, 2010 at 4:50 Comment(5)
Is request available inside the clean_xxx validation methods?Slumberland
In what method should this go?Kleptomania
@JonMcClung Inside def post(self, request, *args, **kwargs).Illogic
Override ModelAdmin._changeform_view if you want it Admin native with authentication.Morrell
THANK YOU @Damon from 13 years ago!!! I've been working on this for hours. This is so elegant and simple.Uninhibited
P
116

You can use self.data in the clean_email method to access the POST data before validation. It should contain a key called newsletter_sub or newsletter_unsub depending on which button was pressed.

# 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
Prejudice answered 14/5, 2009 at 23:3 Comment(5)
can you give an annotated example? it'd really helpJemma
I don't think clean isn't really the right kind of place to do model-level logic. It's specifically for cleaning a form and finding any ValidationErrors that span multiple inputs. Sven's answer is more in the right direction, but still not necessarily the best answerRealgar
Security Risk! Using form data to make changes in the database before the form has been validated is dangerous.Team
You may want to run different types of validation depending on which button was pressed. In that case, there is no "Security" impact.Slumberland
How would you test this using TestCase.client.post?Hushhush
G
39

You can also do like this,

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

CODE

if request.method=='POST' and 'btnform1' in request.POST:
    do something...
if request.method=='POST' and 'btnform2' in request.POST:
    do something...
Granada answered 3/6, 2013 at 7:9 Comment(1)
form1.as_p when to use this?? in the views, while checking validity of form, I wish I could do... form1.is_valid() then do this, form2.is_valid() do this..Needlework
P
8

one url to the same view! like so!

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>
Pulpiteer answered 29/3, 2017 at 3:42 Comment(4)
how should i refer to particular view from other html files href = "{% url 'appname:viewname' %}"Container
what should i give for form in views.pyContainer
Why are you testing if the method is POST in the post function? Class based views only call post function if the http method is POST.Thi
I can't seem to remember as to why seeing as this was 4 years ago. However, I seemed to recall that this was necessary because of a circular action method with one form being submitted and not the other. Not entirely sure though.Pulpiteer
T
5

It's an old question now, nevertheless I had the same issue and found a solution that works for me: I wrote 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
Trochal answered 6/5, 2014 at 22:0 Comment(1)
Where and how exactly will you use MultiRedirectMixin? Please elaborate with an example.Dicephalous
F
2

I know this is old, but some of the answers are, to say the least, brief, and they do not address a common case where the form is not a django form.

This solution was inspired by this blog post. It relies on using a view class that is derived from django.views.generic.edit.FormMixin, e.g. CreateView, UpdateView or DeleteView. These provide the get_success_url method which exposes the button name in 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 ''
Femme answered 2/8, 2021 at 11:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.