Can I make my page return with the information previously entered when the form is invalid?
Views.py
def MyView(request):
[...code...]
if request.method == 'POST':
form = MyForm(request.POST or None)
if form.is_valid()
//do something and redirect to success page
else:
//back to the page with the information filled in by the user (HERE IS MY PROBLEM)
the line form = MyForm(request.POST or None) creates a MyForm object called form with request data assigned to it - its kinda like filled out form. The is_valid() method checks for errors in your form and adds particular errors to your form so this is now a filled out form with errors assigned. If you want to return this form to user you should add it to context so considering its a standard django function based view it should look like this:
def MyView(request):
[...code...]
if request.method == 'POST':
form = MyForm(request.POST or None)
if form.is_valid()
form.save()
return render(request, 'succes_page_template.html')
else:
return render(request, 'current_template.html', context = {'form': form})
if the form is invalid the next thing the user sees is same page where he filled out the form ('current_template.html') but with all the fields filled with data he/she already put it also form will have erros assigned to particular fields so you can print them. If you are new to Django I suggest getting into class based views from the start - they do the heavy lifting for you and refactoring + debugging becomes much easier. here is the link cheers!
When i want to check if form data was submitted using POST, what is the difference between:
if request.method == 'POST'
And:
if form.validate_on_submit():
?
form.validate_on_submit() does what the request.method == 'POST' and more, so it's like a more advanced function.
form.validate_on_submit() is a function implemented in the "Flask-WTF" package, which is a shortcut to two functions: form.is_submitted() and form.validate()
def validate_on_submit(self):
"""Call :meth:`validate` only if the form is submitted.
This is a shortcut for ``form.is_submitted() and form.validate()``.
"""
return self.is_submitted() and self.validate()
Note: The self.validate() is implemented by the "wtforms" package, which "Flask-WTF" uses the wtforms.Form as a base class for the flask_wtf.FlaskForm or the flask_wtf.Form which is deprecated.
Going deeper, what the self.is_submitted() function does is return a _is_submitted() boolean function:
def is_submitted(self):
"""Consider the form submitted if there is an active request and
the method is ``POST``, ``PUT``, ``PATCH``, or ``DELETE``.
"""
return _is_submitted()
And the _is_submitted() is defined as:
def _is_submitted():
"""Consider the form submitted if there is an active request and
the method is ``POST``, ``PUT``, ``PATCH``, or ``DELETE``.
"""
return bool(request) and request.method in SUBMIT_METHODS
The SUBMIT_METHODS constant is defined this way:
SUBMIT_METHODS = set(('POST', 'PUT', 'PATCH', 'DELETE'))
From the snippets, you can see that the form.validate_on_submit() handles the request.method and does more.
So if you are making use of the Flask-WTF package and your form is inheriting from the "FlaskForm" class, it's better to use form.validate_on_submit() than request.method == 'POST', as the former handles both verifying if a form is submitted and if the post request made is also valid.
Hy there,
i just read here that posted data can't be sent by a redirect,
and then found that people are generally not happy with using redirects. And my question is why?
My situation
I have a Django app which starts by rendering a page which gives the choice of using Registered or Anonymous usage.
def start(request):
request.session.flush()
return render_to_response('start.html')
If Registered usage is chosen a simple form is rendered to insert the registration data, and it loops to itself until form is valid after which 'auth/' takes place.
def registration(request):
if request.method == "POST":
form = RegistrationForm(request.POST)
if form.is_valid():
return HttpResponseRedirect('auth/')
else:
return render_to_response('registration.html',
{'form':form})
form = RegistrationForm()
return render_to_response('registration.html',
{'form':form})
If anonymous usage is chosen, the registration page is skipped, and is redirected to 'auth/', the same Django view function like in upper code:
return HttpResponseRedirect('auth/')
What i was hoping to achieve with auth is for it to just set the sessions, write the data to a database and then redirect the next page which is identical to Registered and Anonymous users (only the sessions would differ).
def auth(request):
ipdb.set_trace()
if request.method == "POST":
request.session['user_type'] = 'REG'
request.session['email'] = request.POST['email']
request.session['first_name'] = request.POST['first_name']
RegisteredUser.objects.create(first_name = request.POST['first_name'],
email = request.POST['email'],
company = request.POST['company'])
else:
request.session['user_type'] = 'ANONIM'
return HttpResponseRedirect('next_page')
Of course, when the debugger starts, request.method always returns GET.
My reasons
There is a lots of talking about separating the view from the logic, and i don't find it very readable or loosely coupled when i have a Django view function (if I drop the 'auth/', 'next_page' would have to take it's functionality) which has to
Check for request.post
If there if the reason for it is form validation of the current page, or if it's from the previous page, in which case write that data to a base
Do additional checks to set the sessions for registered or anonymous user
which I would have to do since using separated functions just for logic that redirects instead of rendering doesn't seem as the generally accepted way.
Can someone please shed some light on things?
UPDATE WITH SOLUTION
Thanks to the answers i figured out the pattern on how to do this. The code for registration is now:
def registration(request):
if request.method == "POST":
form = RegistrationForm(request.POST)
if form.is_valid():
request.session['user_type'] = 'REG'
request.session['email'] = request.POST['email']
request.session['first_name'] = request.POST['first_name']
RegisteredUser.objects.create(first_name = request.POST['first_name'],
email = request.POST['email'],
company = request.POST['company'])
return HttpResponseRedirect('param_select/')
else:
return render_to_response('registration.html',
{'form':form},
context_instance = RequestContext(request))
form = RegistrationForm()
return render_to_response('registration.html',
{'form':form},
context_instance = RequestContext(request))
From now on, I'm using the "Loop into yourself with POST data, and then perform the logic" method.
To demystify the name, thats what the new registration(request) does.
First it renders the form to input the data
Than it is recalled once the data is submitted because of link to itself
form action="" method="post"
This is repeated until correct form data isn't submitted after which the logic is called (in this case, writing to a base and setting the sessions).
It ends with a httpRedirect to a different page leaving the registration completely handled by registration(request).
By default you can assume that user is anonymous and set request.session['user_type'] = 'ANONIM' in your start() view.
Whatever you are doing in auth() you can do it in registration() along with request.session['user_type'] = 'REG'. With this you can use the validated form to create RegisteredUser and flag errors as well if required.
And once POST processing is valid and complete it can redirect to your next_page.
If anonymous user is chosen, start will redirect to next_page rather than auth/
I have a form which contains an imagefield and some other fields.
my view looks like this:
def post_view(request):
def errorHandle(errors,form):
return render_to_response('post_form.html', {
'errors':errors,
'form' : form,
},context_instance=RequestContext(request))
if request.method == 'POST': # If the form has been submitted...
form = PostForm(request.POST, request.FILES) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
#save
else:
errors = form.errors
return errorHandle(errors,form)
else:
form = PostForm() # An unbound form
sucess = ''
return render_to_response('post_form.html', {
'form': form,
}, context_instance=RequestContext(request))
When errorHandle is called, it returns to the page with the form, errors and the values entered except for the value entered on an ImageField.
tell me if you need to look at my form and model. thanks in advance.
I think it's normal.
Path of a client image is secure for a backend, so, it was not sent. It's an official HTTP feature.
The suggested pattern for processing a form in a view seems overly complex and non-DRY to me:
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return render_to_response('contact.html', {
'form': form,
})
That's a lot of conditionals, it repeats the ContactForm() construction, and the whole block is repeated everywhere a view needs to process a form. Isn't there a better way of doing it?
You can avoid the repetition, of course. Mostly, you need to pass in as arguments the class of form and template name to use, a callable to process the cleaned data when a valid form is submitted, and a destination for the redirect after such processing; plus, you need a little extra code to call the form class just once, to produce either a bound or unbound form, and deal with it properly. I.e.:
def process_any_form(request,
form_class, template_file_name,
process_data_callable, redirect_destination):
form = form_class(request.POST if request.method == 'POST' else None)
if form.is_bound and form.is_valid():
process_data_callable(form.cleaned_data)
return HttpResponseRedirect(redirect_destination)
return render_to_response(template_file_name, {'form': form})
You are right it could be better, here is a better alternative (but keep reading):
def contact(request):
form = ContactForm(request.POST or None) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
return render_to_response('contact.html', {
'form': form,
})
This snippet comes from a talk called Advanced Django Form Usage from DjangoCon11.
Note that this will process an empty form as valid (even before submission) if all the fields are optional and you don't use CSRF protection. So to eliminate that risk, you better use this one:
def contact(request):
form = ContactForm(request.POST or None) # A form bound to the POST data
if request.method == 'POST' and form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
return render_to_response('contact.html', {
'form': form,
})
The boilerplate way of processing forms mixes two concerns: presenting a form to edit and processing the results. You could break this into two methods, which would introduce some duplication in the form of identical render_to_response() calls. By the time you refactored that, you might end up with something that's less readable than the single-method form above.
When I look at the boilerplate method, I don't see duplication. The two uses of ContactForm() are distinctly different. The two conditionals seem to me to fairly cleanly show the state transitions involved in processing a form (present a blank form, accept submissions until one is valid, process-and-redirect).
Alex's generic handler beat me to it, but FWIW we tend toward a less-generic version of his suggestion:
def contact(request):
post_data = request.POST if request.method == 'POST' else None
form = ContactForm(post_data)
if request.method == 'POST':
# perform normal validation checking, etc
return render_to_response('contact.html', {
'form': form,
})
If post_data is None, then the form is instantiated as being unbounded. Otherwise, bound processing continues as normal. It avoids a duplicated construction of ContactForm, but I agree with Dave's answer that the duplicate construction doesn't bother me as being a duplicate precisely because the construction parameters are different.
I got so tired of this that i wrote my own generic views to handle it. In the process, I discovered that django already has underdocumented generic views for forms processing. They are fairly direct analogues of the documented generic views, but accept forms, and basically follow the same template you used in your example. Ultimately, I found them too inflexible and stupid for my use (I don't want a create_or_update view, I wan't to treat those two actions seperately.)
Edit: You didn't like Fragsworth's answer, which points to the same thing i'm talking about, I assume you wont' like mine either. Here's an example for how it works.
# in urls.py
urlpatterns += patterns("",
(u'^...$', 'django.views.generic.create_update.update', {
'form_class': ContactForm })
)
ContactForm must have a save() method, and thats where your form processing logic goes.
One could write a function that handles the conditionals for all forms. You could do this by passing in a function specific to that form after "is_valid", such as:
def FormHandler(request, CleaningFunction, redirecturl):
if request.method = 'POST':
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
CleaningFunction(form) # Process the data in form.cleaned_data
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return form
Then you would call FormHandler from your view. Note this isn't tested and may have errors.
You can bypass django's forms module and just do it the old fashion way, you get more flexibility without too much loss IMHO.
Last time I looked at django forms was quite a while ago, I don't know if things have changed, but for instance, it doesn't really allow you build an ajax-style form; at least not easily.
Django provides several generic views for creating, editing, and deleting objects. Perhaps you could try these.