After submitting a form that contains errors, the incorrect fields get marked as such. Correctly submitted fields, however, also get marked.
Is there a way to prevent this from happening? I'd prefer it if Django were to only render the incorrect fields differently, and render the correctly submitted fields as normal.
I checked the API offered by the Form object, but there does not seem to be a property that lists these correctly submitted fields.
Django by default only marks the invalid fields, not the valid ones.
Be sure you are passing the POST data to the form in the view when POST.
(incomplete example below)
if request.method == 'POST':
form = YourForm(request.POST)
if form.is_valid():
# your code then redirect
else: #GET
form = YourForm()
You can take a look to this Django example in the docs for a full example.
Related
I have a Django form that takes filter options for a report. The report page is a separate view that renders the report based on the form data.
My first pass at this, I simply set the action of the form to the report page and method to GET. The form data was then passed directly the report view via the querystring which I would use GET to retrieve. The problem with this was that this bypassed form validation since the the form did not post back to its own view.
My next pass, I removed the form action (so it would post back to itself) and used form_valid (I am using class based views) to encode the form data and redirect to the report view like so:
ReportOptionsView(FormView)
form_class = OptionsForm
template_name = 'my_report_options.html'
report = reverse_lazy('my_report')
def form_valid(self, form):
qstr = urlencode(form.cleaned_data)
return redirect(self.report+"?"+qstr)
The report page works the same -I just retrieve the information from the querystring to filter the models and display the report.
I would prefer the form data not appear on the querystring of the report page. When I tried to redirect to the report page using a POST method is where I starting having trouble. Even going back to my original flow setting the form action to the report page (thus losing validation) and setting the form method to POST, I got 405 errors. I realize that there may be ways to do this using Javascript, but would prefer to stick with Django/Python
My question is, what is the proper method in Django to take cleaned data from a validated form and POST that data to separate view so that the form data is not exposed in the URL?
I am building a website and I want various views that will ask the user to request a quote from our page. I want to keep the code as DRY as possible so I am writing a view quote which will receive the quote requests from various views and, if there is a validation error redirect back to the page that made the request. I managed to solve this using the super bad practice 'global variables'. I need a better solution, I would like redirecting to respective view with the current form so I can iterate through the form.errors. Here is my code:
def send_quote(request):
form = Quote(request.POST)
if form.is_valid():
# do stuff when valid
return redirect('Support:thanks', name=name or None)
quote_for = request.POST['for_what']
global session_form
session_form = form
return redirect('Main:' + quote_for) # Here I would like to send form instead of storing in global variable`
You can use the HttpResponseRedirect function, and pass as argument the page that made the request.
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
All the META data is store on a dictionary, if you want to learn more check the documentation.
https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.META
If you redirect to the referrer, form.errors will be empty, as redirection is always a GET request.
I can think of two solutions to your problem:
Submit forms asynchronously using JavaScript and so populate the errors
Make all the views containing the form support POST - one way to do this would be to create a base class that inherits from FormView
The second option is a typical way of handling forms in Django - you process both POST and GET inside the same view.
After two days of searching I finally found the answer. Instead of saving form in request.session I just save request.POST and then redirect. Here is the code:
def send_quote(request):
form = Quote(request.POST)
if form.is_valid():
# do stuff when valid
return redirect('Support:thanks', name=name or None)
quote_for = request.POST['for_what']
request.session['invalid_form'] = request.POST
return redirect('Main:endview')
def endview(request):
session_form = request.session.pop('invalid_form', False)
if session_form:
form = Quote(session_form)
# render template again with invalid form ;)
Now I can repeat this with all the views I want and just change the what_for input of each form to match the respective view (Like I intended).
One view I have is using a modelform with custom field cleaning. One type of cleaning is checking if the user is trying to submit a change to a field that is already set to the value, and it works exactly how I want it to work by throwing a ValidationError. The problem of course is that I can only submit one form at a time, so I'd like to use a modelformset to submit multiple forms.
I know it is possible to override the modelformset's clean method, but I'm asking if it's possible to use the modelform's field cleaning methods on the modelformset?. Currently when I submit the modelformset with empty fields the is_valid() passes which seems strange to me...
I also would like to know "typically" where the custom modelformset validation code would go? I was thinking with the forms.py.
*Edit -- with answer. My httpResponseRedirect was allowing the form to be submitted without validation.
def mass_check_in(request):
# queryset
qs = Part.objects.none()
errlst=[]
c = {}
c.update(csrf(request))
# Creating a model_formset out of PartForm
PartFormSetFactory = modelformset_factory(model=Part,
form=PartForm,
formset=BasePartFormSet,
extra=2)
if request.method == 'POST':
PartFormSet = PartFormSetFactory(request.POST)
if PartFormSet.is_valid():
PartFormSet.save()
return http.HttpResponseRedirect('/current_count/')
else:
PartFormSet = PartFormSetFactory(queryset=qs, initial=[
{'serial_number':'placeholder',
},
{'serial_number':'placeholder'
}])
return render(request,'mass_check_in.html',{
'title':'Add Item',
'formset': PartFormSet,
'formset_errors': PartFormSet.non_form_errors(),
})
If you don't enter any data at all in one of the modelforms in your model formset, it will skip validation for that form; from the docs:
The formset is smart enough to ignore extra forms that were not changed.
You can actually disable this functionality though by forcing empty_permitted=False on the forms; see the accepted answer to this question for more: Django formsets: make first required?
Formsets do have their own clean method, but it's used to validate information between two or more forms in the formset, not for validating the individual forms themselves (which should be taken care of in the forms clean method - as you are doing now.
A formset has a clean method similar to the one on a Form class. This is where you define your own validation that works at the formset level:
Here's another similar question:
Django formset doesn't validate
I've seen Django's samples and I can see they have decent error handling. However I want to see if there is yet a better approach, a general pattern to handle form validation errors in Django. This is the sample I found here:
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,
})
In particular, I was wondering:
How can the view in "/thanks/" be sure that the form was validated? Are there any common ways to pass the successful validation of the form to the next view? Or do I need to do something manually such as setting a flag in request's session?
How can one write this code in a way that when form is NOT valid and the page is shown with errors upon submission, if user refreshes the browser it wouldn't ask the user if they want to POST data again?
EDIT: With regards to #1 I am referring to cases like user manually entering the '/thanks/' url or going back and forth through history pages and accidentally openning it without any form being validated. (Do we still show the "thanks" page? or we need to somehow re-validate why we are in thanks view).
The view can be sure that the form is validated because it will only be called if the form is valid...
If the page is generated through a post request the browser will always ask you that when hitting refresh... I guess the only way to avoid this would be redirecting to another page!
How can the view in "/thanks/" be sure that the form was validated?
form.is_valid() should thoroughly check any field or - if necessary - any combination, cornercase, etc. That's basically it. The views knows, the form was valid if it renders. There is no need to include redundant information in the session.
How can one write this code in a way that when form is NOT valid and the page is shown with errors upon submission, if user refreshes the browser it wouldn't ask the user if they want to POST data again?
I am not sure what the point would be. The form contains errors and the user may correct them or leave. To render a page that would not ask for form resubmission, one could use a redirect, just as in the valid case. The error markup would have to be done manually in that case.
I am accepting data via request.POST like this:
if request.method == 'POST':
l = Location()
data = l.getGeoPoints(request.POST)
appid = settings.GOOGLE_API_KEY
return render_to_response('map.html',
{'data': data, 'appid': appid},
context_instance=RequestContext(request))
It accepts data from a bunch of text input boxes called form-0-location all the way up to form-5-location.
What I want to add in is a check to make sure that request.POST contains data in any of those input fields. I think my problem is that I do not know the correct terminology for describing this in Django.
I know how to do it in PHP: look inside $_POST for at least one of those fields to not be empty, but I can't seem to find the right answer via searching for google.
If I don't find any data in those input fields, I want to redirect the user back to the main page.
Have you thought about using Django's Forms?? You can mark fields as "required" when defining a form and Django will take care of validating if said fields have data in them upon submission. They also do other kinds of validation.
if request.method == 'POST' and request.POST:
# Process request
request.POST will be false if the request does not contain any data.
With Django request objects, the POST data is stored like a dictionary, so if you know the keys in the dictionary, you can search for them and check if they're empty or not. Check out these two links for more detail:
http://docs.djangoproject.com/en/dev/ref/request-response/#attributes
http://docs.djangoproject.com/en/dev/ref/request-response/#querydict-objects
And, for example, when you have your request object, and you know you have a key/var called 'form-0-location', you could do:
if request.POST.get('form-0-location'):
print 'field is not None >> %s' % request.POST.get('form-0-location'')
I second the suggestion to use Django Forms. Why not take advantage of Django Forms when you are using Django?
Design a quick form that matches the fields you currently have on the page. Load the form with request.POST data, and use form.is_valid() to determine whether the form is valid or not .
request.POST returns a type of QueryDict (which extends the Dictionary superclass).
This means you can iterate through the keys in this dictionary (all the parameters in POST) and return false when you see one that is empty
(for key in request.POST):
if key k has invalid value (i.e. None or something else):
return false
return true
You could also try try something like
if len(request.POST['data'])<1:
do something if empty
else:
do something if has data