169

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?

Cedric Druck
  • 978
  • 5
  • 18
veena
  • 1,733
  • 2
  • 11
  • 6

5 Answers5

250

Eg:

if 'newsletter_sub' in request.POST:
    # do subscribe
elif 'newsletter_unsub' in request.POST:
    # do unsubscribe
Damon Abdiel
  • 2,851
  • 2
  • 15
  • 13
108

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
John Mee
  • 44,003
  • 31
  • 133
  • 171
Ayman Hourieh
  • 113,954
  • 21
  • 138
  • 113
  • 4
    can you give an annotated example? it'd really help – Jharwood Oct 25 '12 at 17:55
  • 11
    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 `ValidationError`s that span multiple inputs. Sven's answer is more in the right direction, but still not necessarily the best answer – Patrick Aug 06 '14 at 06:25
  • 9
    Security Risk! Using form data to make changes in the database before the form has been validated is dangerous. – Quant Metropolis Mar 14 '16 at 16:39
  • 1
    You may want to run different types of validation depending on which button was pressed. In that case, there is no "Security" impact. – sureshvv Sep 25 '16 at 10:28
32

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...
  • 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.. – hlkstuv_23900 Oct 29 '14 at 06:31
7

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>
chrisroker0
  • 121
  • 1
  • 6
  • how should i refer to particular view from other html files href = "{% url 'appname:viewname' %}" – uma_8331 Mar 01 '21 at 17:18
  • what should i give for form in views.py – uma_8331 Mar 02 '21 at 02:44
  • 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. – Braiam Mar 13 '21 at 12:24
  • 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. – chrisroker0 Mar 14 '21 at 13:30
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
Sven
  • 915
  • 10
  • 25