Django: Passing value from template to view - django

I HAD this situation:
Clicking on a html submit button, I call views.stream_response which "activates" views.stream_response_generator which "activates" stream.py and return a StreamingHttpResponse and I see a progressive number every second up to m at /stream_response/:
1
2
3
4
5
6
7
8 //e.g. my default max value for m
stream.py
from django.template import Context, Template
import time
def streamx(m):
lista = []
x=0
while len(lista) < m:
x = x + 1
time.sleep(1)
lista.append(x)
yield "<div>%s</div>\n" % x #prints on browser
print(lista) #print on eclipse
return (x)
views.py
def stream_response(request): // unified the three functions as suggested
if request.method == 'POST':
form = InputNumeroForm(request.POST)
if form.is_valid():
m = request.POST.get('numb', 8)
resp = StreamingHttpResponse(stream.streamx(m))
return resp
forms.py
from django.db import models
from django import forms
from django.forms import ModelForm
class InputNumero(models.Model):
m = models.IntegerField()
class InputNumeroForm(forms.Form):
class Meta:
models = InputNumero
fields = ('m',)
urls.py
...
url(r'^homepage/provadata/$', views.provadata),
url(r'^stream_response/$', views.stream_response, name='stream_response'),
...
homepage/provadata.html
<form id="streamform" action="{% url 'stream_response' %}" method="POST">
{% csrf_token %}
{{form}}
<input id="numb" type="number" />
<input type="submit" value="to view" id="streambutton" />
</form>
If I delete "8" and use only m = request.POST.get('numb') I obtain:
ValueError at /stream_response/ The view homepage.views.stream_response didn't return an HttpResponse object.
It returned None instead.
So, if I try to submit, it takes only the default value 8 (and works) but it not takes my form input. What is it wrong?
-->UPDATE: with #Tanguy Serrat suggestions:
views.py
def stream_response(request):
form = InputNumeroForm()
if request.method == 'POST':
form = InputNumeroForm(data=request.POST)
if form.is_valid():
#Accessing the data in cleaned_data
m = form.cleaned_data['numero']
print("My form html: %s" % form)
print ("My Number: %s" % m) #watch your command line
print("m = ", m)
resp = StreamingHttpResponse(stream.streamx(m))
return resp
#If not post provide the form here in the template :
return render(request, 'homepage/provadata.html', {'form': form,})
forms.py
class InputNumeroForm(forms.Form):
numero = models.IntegerField()
homepage/provadata.py
<form action="/stream_response/" method="POST">
{% csrf_token %}
{{form}} <!--issue: does not appear on the html !!!!!-->
<input type="number" name="numero" /> <!--so I write this-->
<input type="submit" value="to view" />
</form>
If I give as input e.g. 7 from keyboard:
KeyError at /stream_response/
'numero'
WHILE
If I write m = request.POST.get('numero'), in command line I have:
...
My form html:
My Number: 7
m = 7
...
while len(lista) < m:
TypeError: unorderable types: int() < str()

EDIT : ModelForm part removed, no need to save the data in DB so using classic form this way :
Method 1 : Classic Form without Model using Django
in your forms.py
from django import forms
class InputNumeroForm(forms.Form):
numero = forms.IntegerField()
in your views.py
from django.shortcuts import render
def stream_response(request):
form = InputNumeroForm()
if request.method == 'POST':
form = InputNumeroForm(data=request.POST)
if form.is_valid():
#Accessing the data in cleaned_data
m = form.cleaned_data['numero']
print "My Number %s" % m #watch your command line
resp = StreamingHttpResponse(stream.streamx(m))
return resp
#If not post provide the form here in the template :
return render(request, 'homepage/provadata.html', {
'form': form,
})
in your template :
<form id="streamform" action="{% url 'stream_response' %}" method="POST">
{% csrf_token %}
{{ form }}
<input type="submit" value="to view" id="streambutton" />
</form>
To clarify a little there are two types of form in Django :
Classic Forms which do not require saving in a database
Model Forms which will allow you to create forms which are based on a Database Model i.e. (you can add a row to your db or edit one)
Here you don't need to save your number in your database so you use classical forms:
https://docs.djangoproject.com/en/1.8/topics/forms/
Method 2 : Not using the Django Form
For very simple forms like your case :
in views.py
from django.shortcuts import render
def stream_response(request):
if request.method == 'POST':
if request.POST.get('numero', False):
m = int(request.POST['numero'])
print "My Number %s" % m #watch your command line
resp = StreamingHttpResponse(stream.streamx(m))
return resp
return render(request, 'homepage/provadata.html')
in your template :
<form id="streamform" action="{% url 'stream_response' %}" method="POST">
{% csrf_token %}
<input type="number" name="numero" />
<input type="submit" value="to view" id="streambutton" />
</form>

I see a handful of issues with the code you posted:
Your form field is missing the 'name' attribute, and thus the value set for this field will not be passed to your code. This is why your request.POST.get('numb') returns None unless you provide your default (8).
Try this:
<input id="numb" name="numb" type="number" />
Also, I noticed in your form you use models.IntegerField - why are you trying to use a model field here?
A better approach for you may be to add the numb field to your form and then retrieve the value from the form's cleaned data: form.cleaned_data['numb'] instead of the POST data.
Hopefully this gets you going.

Django views MUST return a HttpResponse object. Considering that the other two functions you're referencing have only one line each, it would serve you better to just put everything in one function, or at least move the StreamingHttpResponse to the main function.
Edit: The StreamingResponseHttp must be given an iterable so, going back to the stream.py, try taking out the return and print functions (I've taken out excess stuff for experimentation's sake). This worked for me in the shell.
def streamx(m):
lista = []
x=0
while len(lista) < m:
x = x + 1
time.sleep(1)
lista.append(x)
yield "<div>%s</div>\n" % x #prints on browser
#c = Context({'x': x})
#yield Template('{{ x }} <br />\n').render(c)
def stream_response(request):
if request.method == 'POST':
form = InputNumeroForm(request.POST)
if form.is_valid():
m = request['numb']
resp = StreamingHttpResponse(streamx(m))
return resp

Related

Django-forms: Raise forms.ValidationError not working

I am beginner in Django and recently studied form-validation. I implemented the code but was unable to raise ValidationError for some constraints.
Here are my subsequent file content.
forms.py
from django import forms
from django.core import validators
class formClass(forms.Form):
name = forms.CharField(max_length=128)
email = forms.EmailField(max_length=256)
text = forms.CharField(widget=forms.Textarea)
catchBot = forms.CharField(required=False, widget=forms.HiddenInput,
validators=[validators.MaxLengthValidator(0)])
def clean(self):
cleaned_data = super(formClass, self).clean()
t = self.cleaned_data.get('name')
if t[0].lower() != 'd':
raise forms.ValidationError('Name must start with d.')
return cleaned_data
views.py
from django.shortcuts import render
from formApp import forms
from django.http import HttpResponseRedirect
def formNameView(request):
formObj = forms.formClass()
formDict = {'form': formObj}
if request.method == 'POST':
formObj = forms.formClass(request.POST)
if formObj.is_valid():
# SOME CODE
print("NAME: " + formObj.cleaned_data['name'])
print("EMAIL: " + formObj.cleaned_data['email'])
return HttpResponseRedirect('/users')
return render(request, 'formApp/forms.html', context=formDict)
My valid input works great, but it doesn't happen with my invalid input.
for example: if name = 'Alex', it should raise an error. But it doesn't.
Could someone please help me in it?
EDIT:
[Added forms.html and validators callable.]
Previously, I used validators callable to raise ValidationError instead of clean() method. But the results were same.
Here is my code:
def checkForD(value):
if value[0].lower() != 'd':
raise forms.ValidationError('Name must start with d.')
.
.
.
# in my formClass()
name = forms.CharField(max_length=128, validators[checkForD])
...
Forms.html
<body>
<div class='container'>
<div class='jumbotron'>
<h3>Welcome to the form page.</h3>
<h2>Please insert the form.</h2>
</div>
<form method="post">
{{form.as_p}}
{% csrf_token %}
<input type="submit" value="Submit" class="btn btn-primary"/>
</form>
</div>
</body>
In your POST block you've redefined formObj to be the bound form, but you haven't replaced the instance in the context dict - so what is passed to the template is the empty unbound form, and no errors will be shown on that template.
The easiest fix would be to move the definition of the dict to the end of the function:
formDict = {'form': formObj}
return render(request, 'formApp/forms.html', context=formDict)
Now the correct instance will be used and the errors will show.
You can try this: don't define the formDict in post, directly define it in return render
return render(request, 'formApp/forms.html', {'form': formObj})

Bind dynamic choices to ModelForm in Django

I'm trying to bind a dynamic list of choices to a ModelForm. The form is rendered correctly. However, when using the form with a POST Request, I get an empty form back. My goal is to save that form into the database (form.save()). Any help would be much appreciated.
Model
I'm using a multiple choice select field ( https://github.com/goinnn/django-multiselectfield )
from django.db import models
from multiselectfield import MultiSelectField
class VizInfoModel(models.Model):
tog = MultiSelectField()
vis = MultiSelectField()
Forms
class VizInfoForm(forms.ModelForm):
class Meta:
model = VizInfoModel
fields = '__all__'
def __init__(self,choice,*args,**kwargs):
super(VizInfoForm, self).__init__(*args,**kwargs)
self.fields['tog'].choices = choice
self.fields['vis'].choices = choice
View
Choices are passed from the view when instantiating the form.
def viz_details(request):
options = []
headers = request.session['headers']
for header in headers :
options.append((header, header))
if request.method == 'POST':
form = VizInfoForm(options, request.POST)
#doesnt' get into the if statement since form is empty!
#choices are not bounded to the model although the form is perfectly rendered
if form.is_valid():
form.save()
return HttpResponseRedirect('/upload')
else:
#this works just fine
form = VizInfoForm(options)
return render(request, 'uploads/details.html', {'form': form})
Template
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>Choose variables to toggle between</p>
{{ form.tog }}
<br></br>
<p>Choose variable to be visualized</p>
{{ form.vis }}
<br></br>
<button type="submit">Submit</button>
</form>
You're saying Django doesn't get into your if request.method == 'POST' block.
This tells us that you're not sending your request through the POST method. Your template probably has an error in it, maybe you haven't specified the method on your form, or you made your button to just be a link instead of a submit ?
Show your template so we can say more, unless this was enough to solve your question !

Django custom form validation in ListView

I am using a ListView to set a form and to show results. However i am not sure how can I make form validation and having the same form with errors in case form.is_valid() is not True.
this is my code
forms.py
class InsolventiForm(forms.Form):
anno_validator = RegexValidator(r'[0-9]{4}', 'L\'anno deve essere un numero di 4 caratteri')
anno = forms.CharField(label='Anno', required=True, max_length=4,validators=[anno_validator])
def clean_anno(self):
anno = self.cleaned_data['anno']
return anno
views.py
from .forms import InsolventiForm
class InsolventiView(LoginRequiredMixin, ListView):
template_name = 'insolventi.html'
model = Archivio
form_class = InsolventiForm
def get(self, request):
import datetime
if self.request.GET.get('anno'):
form = self.form_class(self.request.GET)
if form.is_valid():
date = '31/12/'+self.request.GET.get('anno')
dateTime = datetime.datetime.strptime(date, "%d/%m/%Y")
dateC = '01/01/'+self.request.GET.get('anno')
dateTimeC = datetime.datetime.strptime(dateC, "%d/%m/%Y")
context = Archivio.objects.filter(~Q(quoteiscrizione__anno_quota__exact=self.request.GET.get('anno')) \
& Q(data_iscrizione__lte=dateTime) \
& (Q(cancellato__exact=False) | (Q(cancellato__exact=True) & (Q(data_canc__gte=dateTimeC)))))
self.request.session['insolventi_queryset'] = serialize('json', context)
return render(request, self.template_name, {'form':form})
else: return redirect(reverse('insolventi'))
return render(request, self.template_name, {'form':self.form_class()})
this is my template and I am displaying the form manually.
insolventi.html
<form method="get" action="">
{% for field in form %}
{{ field.errors }}
{{ field.as_widget() }}
{% endfor %}
<input type="submit" value="Ricerca" />
</form>
Even if there are errors and form.is_valid() is returning False (giving me a redirect to the same view) on the template I never get {{ form.errors }}.
I don't know what is missing!
I am thinking: Because i use the input of the form to get the query in JSON with django rest and post it on the same template with DataTables, maybe I do not need to use a ListView ??
You should not be redirecting if there are errors since redirecting will lose all the form data.
Try removing the line:
else: return redirect(reverse('insolventi'))
and letting it fall through to the render() line.
Hi can you try this post
custom form validation
also refer django document
django custom validation as per document

how to get different UI elements by the choicefield value?

Here's my code, something is wrong in the test.html if statement. Could you help me to modified the if condition in test.html that I can get different UI element by the choice-field value?
forms.py
class testForm(forms.Form):
runtype = forms.ChoiceField(choices=[(0, 0), (1, 1)])
byname = forms.CharField()
byindex = forms.IntegerField()
views.py
#csrf_exempt
def osys(request):
if request.method == 'POST':
form = testForm(request.POST, request.FILES)
if form.is_valid():
runtype = form.cleaned_data['runtype']
if runtype == 0:
byname = form.cleaned_data['byname']
if runtype == 1:
byindex = form.cleaned_data['byindex']
print byname, byindex, runtype, filename
return HttpResponse('OK')
else:
form = testForm()
return render_to_response('test.html', locals())
test.html
<form method="post" >
{{form.runtype}}
{% if form.runtype ==0 %}
{{form.byname}}
{% if form.runtype ==1 %}
{{form.byindex}}
<input type="submit" value="Start Run" name="btnRun"></input>
</form>
Django templates are rendered server side to plain HTML which is sent to the client which has no knowledge of the template code.
If you need to do things dynamically client side, you'll need to use JavaScript.

Proper way to handle multiple forms on one page in Django

I have a template page expecting two forms. If I just use one form, things are fine as in this typical example:
if request.method == 'POST':
form = AuthorForm(request.POST,)
if form.is_valid():
form.save()
# do something.
else:
form = AuthorForm()
If I want to work with multiple forms however, how do I let the view know that I'm submitting only one of the forms and not the other (i.e. it's still request.POST but I only want to process the form for which the submit happened)?
This is the solution based on the answer where expectedphrase and bannedphrase are the names of the submit buttons for the different forms and expectedphraseform and bannedphraseform are the forms.
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')
You have a few options:
Put different URLs in the action for the two forms. Then you'll have two different view functions to deal with the two different forms.
Read the submit button values from the POST data. You can tell which submit button was clicked: How can I build multiple submit buttons django form?
A method for future reference is something like this. bannedphraseform is the first form and expectedphraseform is the second. If the first one is hit, the second one is skipped (which is a reasonable assumption in this case):
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')
I needed multiple forms that are independently validated on the same page. The key concepts I was missing were 1) using the form prefix for the submit button name and 2) an unbounded form does not trigger validation. If it helps anyone else, here is my simplified example of two forms AForm and BForm using TemplateView based on the answers by #adam-nelson and #daniel-sokolowski and comment by #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>
Wanted to share my solution where Django Forms are not being used.
I have multiple form elements on a single page and I want to use a single view to manage all the POST requests from all the forms.
What I've done is I have introduced an invisible input tag so that I can pass a parameter to the views to check which form has been submitted.
<form method="post" id="formOne">
{% csrf_token %}
<input type="hidden" name="form_type" value="formOne">
.....
</form>
.....
<form method="post" id="formTwo">
{% csrf_token %}
<input type="hidden" name="form_type" value="formTwo">
....
</form>
views.py
def handlemultipleforms(request, template="handle/multiple_forms.html"):
"""
Handle Multiple <form></form> elements
"""
if request.method == 'POST':
if request.POST.get("form_type") == 'formOne':
#Handle Elements from first Form
elif request.POST.get("form_type") == 'formTwo':
#Handle Elements from second Form
Django's class based views provide a generic FormView but for all intents and purposes it is designed to only handle one form.
One way to handle multiple forms with same target action url using Django's generic views is to extend the 'TemplateView' as shown below; I use this approach often enough that I have made it into an Eclipse IDE template.
class NegotiationGroupMultifacetedView(TemplateView):
### TemplateResponseMixin
template_name = 'offers/offer_detail.html'
### ContextMixin
def get_context_data(self, **kwargs):
""" Adds extra content to our template """
context = super(NegotiationGroupDetailView, self).get_context_data(**kwargs)
...
context['negotiation_bid_form'] = NegotiationBidForm(
prefix='NegotiationBidForm',
...
# Multiple 'submit' button paths should be handled in form's .save()/clean()
data = self.request.POST if bool(set(['NegotiationBidForm-submit-counter-bid',
'NegotiationBidForm-submit-approve-bid',
'NegotiationBidForm-submit-decline-further-bids']).intersection(
self.request.POST)) else None,
)
context['offer_attachment_form'] = NegotiationAttachmentForm(
prefix='NegotiationAttachment',
...
data = self.request.POST if 'NegotiationAttachment-submit' in self.request.POST else None,
files = self.request.FILES if 'NegotiationAttachment-submit' in self.request.POST else None
)
context['offer_contact_form'] = NegotiationContactForm()
return context
### NegotiationGroupDetailView
def post(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
if context['negotiation_bid_form'].is_valid():
instance = context['negotiation_bid_form'].save()
messages.success(request, 'Your offer bid #{0} has been submitted.'.format(instance.pk))
elif context['offer_attachment_form'].is_valid():
instance = context['offer_attachment_form'].save()
messages.success(request, 'Your offer attachment #{0} has been submitted.'.format(instance.pk))
# advise of any errors
else
messages.error('Error(s) encountered during form processing, please review below and re-submit')
return self.render_to_response(context)
The html template is to the following effect:
...
<form id='offer_negotiation_form' class="content-form" action='./' enctype="multipart/form-data" method="post" accept-charset="utf-8">
{% csrf_token %}
{{ negotiation_bid_form.as_p }}
...
<input type="submit" name="{{ negotiation_bid_form.prefix }}-submit-counter-bid"
title="Submit a counter bid"
value="Counter Bid" />
</form>
...
<form id='offer-attachment-form' class="content-form" action='./' enctype="multipart/form-data" method="post" accept-charset="utf-8">
{% csrf_token %}
{{ offer_attachment_form.as_p }}
<input name="{{ offer_attachment_form.prefix }}-submit" type="submit" value="Submit" />
</form>
...
This is a bit late, but this is the best solution I found. You make a look-up dictionary for the form name and its class, you also have to add an attribute to identify the form, and in your views you have to add it as a hidden field, with the form.formlabel.
# form holder
form_holder = {
'majeur': {
'class': FormClass1,
},
'majsoft': {
'class': FormClass2,
},
'tiers1': {
'class': FormClass3,
},
'tiers2': {
'class': FormClass4,
},
'tiers3': {
'class': FormClass5,
},
'tiers4': {
'class': FormClass6,
},
}
for key in form_holder.keys():
# If the key is the same as the formlabel, we should use the posted data
if request.POST.get('formlabel', None) == key:
# Get the form and initate it with the sent data
form = form_holder.get(key).get('class')(
data=request.POST
)
# Validate the form
if form.is_valid():
# Correct data entries
messages.info(request, _(u"Configuration validée."))
if form.save():
# Save succeeded
messages.success(
request,
_(u"Données enregistrées avec succès.")
)
else:
# Save failed
messages.warning(
request,
_(u"Un problème est survenu pendant l'enregistrement "
u"des données, merci de réessayer plus tard.")
)
else:
# Form is not valid, show feedback to the user
messages.error(
request,
_(u"Merci de corriger les erreurs suivantes.")
)
else:
# Just initiate the form without data
form = form_holder.get(key).get('class')(key)()
# Add the attribute for the name
setattr(form, 'formlabel', key)
# Append it to the tempalte variable that will hold all the forms
forms.append(form)
I hope this will help in the future.
view:
class AddProductView(generic.TemplateView):
template_name = 'manager/add_product.html'
def get(self, request, *args, **kwargs):
form = ProductForm(self.request.GET or None, prefix="sch")
sub_form = ImageForm(self.request.GET or None, prefix="loc")
context = super(AddProductView, self).get_context_data(**kwargs)
context['form'] = form
context['sub_form'] = sub_form
return self.render_to_response(context)
def post(self, request, *args, **kwargs):
form = ProductForm(request.POST, prefix="sch")
sub_form = ImageForm(request.POST, prefix="loc")
...
template:
{% block container %}
<div class="container">
<br/>
<form action="{% url 'manager:add_product' %}" method="post">
{% csrf_token %}
{{ form.as_p }}
{{ sub_form.as_p }}
<p>
<button type="submit">Submit</button>
</p>
</form>
</div>
{% endblock %}
Based on this answer by #ybendana:
Again, we use is_bound to check if the form is capable of validation. See this section of the documentation:
Bound and unbound forms
A Form instance is either bound to a set of data, or unbound.
If it’s bound to a set of data, it’s capable of validating that data and rendering the form as HTML with the data displayed in the HTML.
If it’s unbound, it cannot do validation (because there’s no data to validate!), but it can still render the blank form as HTML.
We use a list of tuples for form objects and their details allowing for more extensibility and less repetition.
However, instead of overriding get(), we override get_context_data() to make inserting a new, blank instance of the form (with prefix) into the response the default action for any request. In the context of a POST request, we override the post() method to:
Use the prefix to check if each form has been submitted
Validate the forms that have been submitted
Process the valid forms using the cleaned_data
Return any invalid forms to the response by overwriting the context data
# views.py
class MultipleForms(TemplateResponseMixin, ContextMixin, View):
form_list = [ # (context_key, formcls, prefix)
("form_a", FormA, "prefix_a"),
("form_b", FormB, "prefix_b"),
("form_c", FormC, "prefix_c"),
...
("form_x", FormX, "prefix_x"),
]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# Add blank forms to context with prefixes
for context_key, formcls, prefix in self.form_list:
context[context_key] = formcls(prefix=prefix)
return context
def post(self, request, *args, **kwargs):
# Get object and context
self.object = self.get_object()
context = self.get_context_data(object=self.object)
# Process forms
for context_key, formcls, prefix in self.form_list:
if prefix in request.POST:
# Get the form object with prefix and pass it the POST data to \
# validate and clean etc.
form = formcls(request.POST, prefix=prefix)
if form.is_bound:
# If the form is bound (i.e. it is capable of validation) \
# check the validation
if form.is_valid():
# call the form's save() method or do whatever you \
# want with form.cleaned_data
form.save()
else:
# overwrite context data for this form so that it is \
# returned to the page with validation errors
context[context_key] = form
# Pass context back to render_to_response() including any invalid forms
return self.render_to_response(context)
This method allows repeated form entries on the same page, something I found did not work with #ybendana's answer.
I believe it wouldn't be masses more work to fold this method into a Mixin class, taking the form_list object as an attribute and hooking get_context_data() and post() as above.
Edit: This already exists. See this repository.
NB:
This method required TemplateResponseMixin for render_to_response() and ContextMixin for get_context_data() to work. Either use these Mixins or a CBV that descends from them.
If you are using approach with class-based views and different 'action' attrs i mean
Put different URLs in the action for the two forms. Then you'll have two different view functions to deal with the two different forms.
You can easily handle errors from different forms using overloaded get_context_data method, e.x:
views.py:
class LoginView(FormView):
form_class = AuthFormEdited
success_url = '/'
template_name = 'main/index.html'
def dispatch(self, request, *args, **kwargs):
return super(LoginView, self).dispatch(request, *args, **kwargs)
....
def get_context_data(self, **kwargs):
context = super(LoginView, self).get_context_data(**kwargs)
context['login_view_in_action'] = True
return context
class SignInView(FormView):
form_class = SignInForm
success_url = '/'
template_name = 'main/index.html'
def dispatch(self, request, *args, **kwargs):
return super(SignInView, self).dispatch(request, *args, **kwargs)
.....
def get_context_data(self, **kwargs):
context = super(SignInView, self).get_context_data(**kwargs)
context['login_view_in_action'] = False
return context
template:
<div class="login-form">
<form action="/login/" method="post" role="form">
{% csrf_token %}
{% if login_view_in_action %}
{% for e in form.non_field_errors %}
<div class="alert alert-danger alert-dismissable">
{{ e }}
<a class="panel-close close" data-dismiss="alert">×</a>
</div>
{% endfor %}
{% endif %}
.....
</form>
</div>
<div class="signin-form">
<form action="/registration/" method="post" role="form">
{% csrf_token %}
{% if not login_view_in_action %}
{% for e in form.non_field_errors %}
<div class="alert alert-danger alert-dismissable">
{{ e }}
<a class="panel-close close" data-dismiss="alert">×</a>
</div>
{% endfor %}
{% endif %}
....
</form>
</div>
Here is simple way to handle the above.
In Html Template we put Post
<form action="/useradd/addnewroute/" method="post" id="login-form">{% csrf_token %}
<!-- add details of form here-->
<form>
<form action="/useradd/addarea/" method="post" id="login-form">{% csrf_token %}
<!-- add details of form here-->
<form>
In View
def addnewroute(request):
if request.method == "POST":
# do something
def addarea(request):
if request.method == "POST":
# do something
In URL
Give needed info like
urlpatterns = patterns('',
url(r'^addnewroute/$', views.addnewroute, name='addnewroute'),
url(r'^addarea/', include('usermodules.urls')),
if request.method == 'POST':
expectedphraseform = ExpectedphraseForm(request.POST)
bannedphraseform = BannedphraseForm(request.POST)
if expectedphraseform.is_valid():
expectedphraseform.save()
return HttpResponse("Success")
if bannedphraseform.is_valid():
bannedphraseform.save()
return HttpResponse("Success")
else:
bannedphraseform = BannedphraseForm()
expectedphraseform = ExpectedphraseForm()
return render(request, 'some.html',{'bannedphraseform':bannedphraseform, 'expectedphraseform':expectedphraseform})
This worked for me accurately as I wanted. This Approach has a single problem that it validates both the form's errors. But works Totally fine.
I discovered a pretty interesting way to send TWO Forms from a single page using the same view. I tried many options but just wanted something that can just work. So here is something that I discovered. But it only works when there are just TWO Forms on a page.
I am using just try and except method to first try first form and if that doesnt works than try second form. This is quiet interesting to know that it works absolutely fine. Don't use it on scalable application as it can create troublesome or may risk the security of the application, else use Class based view to submit mutiple forms or create seperate views for each form.
def create_profile(request):
if request.method=='POST':
try:
biograph = Biography(name=name, email=email, full_name=full_name, slug_name=slug_name, short_bio=short_bio)
biograph.save()
except:
social = SocialMedia(twitter=twitter, instagram=instagram, facebook=facebook, linkedin=linkedin, github=github)
social.save()