How can I build multiple submit buttons django form? - python

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?

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

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

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

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>

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

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

Related

Updating Form.intial does not change intial value in Django template

Goal
I want to retain the query when a search result renders. Here is an example where the search bar contains stackoverflow on the results page.
Failed Solutions
These are minimal code examples
# search_results.html
<form action="/search" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Search">
</form>
# views.py
...
class SearchView(TemplateView):
"""Search view"""
def form_valid(self, form):
"""redirect to the success url with url params"""
quoted_user_query = urllib.parse.quote_plus(form.cleaned_data['user_query'])
return HttpResponseRedirect(''.join([self.get_success_url(), quoted_user_query]))
def get_context_data(self. **kwargs):
context = super().get_context(**kwargs)
user_query = self.request.GET.get('q', None)
form.initial = {'user_query': user_query}
return context
def get_success_url(self):
"""append success url with search query"""
if not self.success_url:
raise ImproperlyConfigured('No success url for {}'.format((str(self))))
return ''.join([str(self.success_url), '?q='])
The first time I search in the browser, the template renders as expected. If I search foo the following is rendered
<form action="/search" method="post">
<input type="text" value="foo" name="search">
<input type="submit" value="Search">
</form>
But searching a second time doesn't change the string in the search bar. If I search bar after searching foo, the search bar's value does NOT change to bar as expected.
<form action="/search" method="post">
<input type="text" value="foo" name="search">
<input type="submit" value="Search">
</form>
What am I doing wrong? Below is the form code.
# forms.py
from django import forms
class SearchBar(forms.Form):
user_query = forms.CharField()
EDIT:
When I added {{ form.initial.user_query }} to the template, the expected value renders as text next to the search bar, but does NOT render in the search bar.
# search_results.html
{{ form.initial.user_query }}
<form action="/search" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Search">
</form>
bar
<form action="/search" method="post">
<input type="text" value="foo" name="search">
<input type="submit" value="Search">
</form>
I don't think you are getting any value with self.request.GET.get('q', None), because name of the input field is user_query in the forms. So try like this:
class SearchView(TemplateView):
"""Search view"""
def get_context_data(self. **kwargs):
context = super().get_context(**kwargs)
user_query = self.request.GET.get('user_query', None)
context['form'] = SearchBar(initial = {'user_query': user_query})
return context
Also change the form method from post to get ie <form method='get' action='/search'>
I found a clunky solution that creates a new instance of the form instead of updating the initial attribute
# views.py
...
class SearchView(TemplateView):
"""Search view"""
extra_context = {'form': SearchBar()}
def get_context_data(self. **kwargs):
context = super().get_context(**kwargs)
user_query = self.request.GET.get('q', None)
if user_query is not None:
context['form'] = self.form_class(data={'user_query': user_query})
return context
...

Form not saving

How can I save django form without validating. I have a simple form. Like i have a dropdown option if a user select a figi it brings a different field from the model payment and if a user select bank teller it brings a different field from the model payment. So I wanna save them. But its not saving when I use form.is_valid() and it still shows me code post 200 but its not in my database and when I remove form.is_valid(), it throws valueError:validation error. Now the imagefield doesn't save
class payments(models.Model):
Amount=models.IntegerField(default=00000)
figiID=models.CharField(default='F-00000',max_length=10,required=False)
Bank_Teller=models.ImageField(upload_to='media/',required=False)
html
<select id='modePayment'>
<option value='test1'>Figi</option>
<option value='test2'>Bank Teller</option>
</select><br><br>
<div class="test1 pricebox">
<form method="post">
{% csrf_token %}
<h6>Amount:{{pDetail.Amount}}</h6>
<h6>Figi-ID{{pDetail.figiID}}</h6>
<button style="background:#4CAF50;color:white;width:150px;height:40px;" value="submit" >Invest</button>
</form>
</div>
<div class="test2 pricebox">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<h6>Amount:{{pDetail.Amount}}</h6>
<h6>Bank Teller{{pDetail.Bank_Teller}}</h6>
<button style="background:#4CAF50;color:white;width:150px;height:40px;" value="submit" >Invest</button>
</form>
</div>
views.py
def dashboard(request):
if request.user.is_authenticated:
allDocs = Registration.objects.all()
pDetail=payment_form()
if request.method=='POST':
pDetail=payment_form(request.POST,request.FILES)
if pDetail.is_valid():
pDetail.save()
context={'doc':allDocs,'pDetail':pDetail,'iDetail':investment}
return render(request,'dashboard.html',context)
else:
return redirect('Tan:login')
You forgot to save the form variable pDetail.
Views.py
def dashboard(request):
if request.user.is_authenticated:
allDocs = Registration.objects.all()
pDetail=payment_form()
if request.method=='POST':
pDetail=payment_form(request.POST,request.FILES)
if pDetail.is_valid():
pDetail=pDetail.save()
pDetail.save() #<----- Add this
context={'doc':allDocs,'pDetail':pDetail,'iDetail':investment}
return render(request,'dashboard.html',context)
else:
return redirect('Tan:login')

How to save changed fields with ModelForm?

I want to save changed values of ModelForm to database. I'm having problems even I follow this documentation if I'm right that it can be possible with initial values: Documentation- providing initial values
models.py:
class Settings(models.Model):
url = models.URLField(max_length=100, default='https://website.com')
maxCount = models.SmallIntegerField(default=30)
views.py:
def Settings(request):
settingsObj = Settings.objects.get(id=1)
form = SettingsForm(initial={'url': settingsObj.url, 'maxCount':settingsObj.maxCount}, instance=settingsObj)
if form.is_valid():
form.save()
forms.py:
class SettingsForm(forms.ModelForm):
class Meta:
model = Settings
fields = ['url', 'maxCount']
templates
<form class="form-horizontal" role="form" method="POST">{% csrf_token %}
<div class="form-group">
<div class="col-sm-offset-1 col-sm-6">
{{ form.as_p }}
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-6">
<button type="submit" class="btn btn-primary btn-lg btn-block">Accept</button>
</div>
</div>
</form>
Currently the form is showing the current values from database, but isn't saving changed data. form.is_valid() returns True, but form.save() seems to do nothing.
The initial argument and the instance argument in your call to SettingsForm() serve the exact same purpose, because you are using the fields of the instance individually in each field of initial.
The save() method is not working because you need to populate the form with data from request.POST.
This view should work:
def settings(request):
settingsObj = Settings.objects.get(id=1)
if request.POST:
form = SettingsForm(request.POST, instance=settingsObj)
if form.is_valid():
form.save()
else:
form = SettingsForm(instance=settingsObj)
context = { ..., 'form': form, ... }
return render(request, 'template-address', context)

Django - Class based views is returning a blank page

I'm trying to use the class based views to create a page where I can delete an article by his ID.
here is the codes to create the pages where I can delete my articles.
views.py
class DeleteView(View):
def post(self, request, *args, **kwargs):
article = get_object_or_404(id=request.POST['article_id'])
article.delete()
return HttpResponseRedirect('/')
template.html
<form action="views.article.DeleteView" method="POST">
{% csrf_token %}
<p>{{ article.views.id }}"</p>
<input type="submit" value="Delete">
</form>
urls.py
url(r'^delete/(?P<id>\d+)/$', DeleteView.as_view(), name="DeleteView"),
Here is my problem > the page is not rendering, I'm getting a blank page without anything on it, how can I fix this?
******* EDIT *******
views.py
class DeleteView(View):
def post(self, request, *args, **kwargs):
article = get_object_or_404(id=request.POST['article_id'])
article.delete()
return HttpResponseRedirect('/')
def get(self, request, *args, **kwargs):
article = Article.objects.get(id=kwargs['id'])
context = {"article": article}
return render_to_response("template.html", context, context_instance=RequestContext(request))
Edit in template file:
<form action="{% url "DeleteView" %}" method="POST">
{% csrf_token %}
<input type="hidden" name="acticle_id" value="{{ article.id }}">
<input type="submit" value="Delete">
</form>
Pleas, read this. The template file should end with html, not py.

Django/Python: How to pass a variable from a form to a python script with POST method?

I'm getting this error when submit:
CSRF verification failed. Request aborted.
I've got this far following the documentation, but I don't fully understand it and it's definitely wrong. I just want to take a query word from my search box(form) and pass it to a python script as an argument. I'm new to Django and getting stuck on the easiest things.
In models.py:
class QueryForm(forms.Form):
query = forms.CharField(label='query',max_length=100)
I added this line to my urls.py
url(r'^results/$', 'tweemo.views.results'),
On my homepage where my search box is I have this code for my form:
<form action="/home/results/" method="post">
<label for="query">Search:</label>
<input id="query" type="text" name="query" value="{{ current_query }}">
<input type="submit" value="ok">
</form>
In views.py I added these two functions:
def get_query(request):
if request.method == 'POST':
form = QueryForm(request.POST)
if form.isvalid():
return HttpResponseRedirect('/thanks/')
else:
form = QueryForm()
return render(request, 'results.html', {'form': form})
def results(request):
return render_to_response('results.html',{'here':TwitterStream.objects.all() })
MY results.html contains just this:
<form action="/home/results/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit"/>
</form>
You must just add the {% csrf_token %} tag inside EVERY <form></form> tag which has method to be post in your template.
So the below markup should be corrected:
<form action="/home/results/" method="post">
{% csrf_token %}
<label for="query">Search:</label>
<input id="query" type="text" name="query" value="{{ current_query }}">
<input type="submit" value="ok">
</form>
Well the problem is that you are not passing the csrf token to the form , you need to pass the csrf token to the render function in order for it to be applied in the form . To accomplish this you need to associate the csrf token to the request.
def get_query(request):
if request.method == 'POST':
form = QueryForm(request.POST)
if form.isvalid():
return HttpResponseRedirect('/thanks/')
else:
form = QueryForm()
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('results.html', args)
def results(request):
return render_to_response('results.html',{'here':TwitterStream.objects.all() })

Categories