Django loading initial data prevents errors being displayed - django

I have a Django page with a form. I have some view code which deals with the form as normal but prepopulates the form with initial data from the a user's session if available. This is so that when a user returns to this form they see previously selected options (yes, the form is quite extensive).
My views,py:
def myView(request):
...
form = ProjectInfoForm(request.POST or None)
if form.is_valid():
# process form, including a redirect
...
# if there is form data in the session, let's use that
# to initaliaze our from with data
if key in request.session:
form = ProjectInfoForm(
initial={
'model': request.session.get('model'),
...
}
)
return render_to_response(template_name, {
...
}, RequestContext(request))
Problem is: if I load the session data then the page does not display any error messages. The form does fail validation, I am just not getting the any output. Is there some conflict here with initial?
Any help would be much appreciated.

Well, from that snippet, if the session data is found then you completely re-initialize the form: the original, validated instance, which contained the POST data and any errors, has now been disposed. Presumably you would want to only enter that second if if the request is not a POST.

Related

Sending form to another view django

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

Django Overwrite form data saved

I've posted about this problem before, but I still haven't found a solution so I'm hoping I'll have better luck this time.
I have a form that takes inputted data by the user. In another page, I am creating the identical form that the user has populated (pre-filled with that information) for editing purposes. Users will come to this page to EDIT the information they have already put in. My problem is that it isn't overwriting the instance.
def edit(request):
a = request.session.get('a', None)
if a is None:
raise Http404('a was not found')
if request.method == 'POST':
form = Name_Form(request.POST, instance=a)
if form.is_valid():
j = form.save( commit=False )
j.save()
else:
form = Name_Form( instance = a )
For this form, I'm using "unique_together" for some of the values. I'm also calling on `{{ form.non_field_errors }} in the template.
What is happening is when I make changes in the editing view, if the fields changes involves those defined in "unique_together" then an error is returned telling me that the instance already exists. Otherwise it saves a new instance. It isn't OVERWRITING.
Note that the reason i am using unique_together is that I want to prevent users from initially inputting the same form twice (before the editing stage, in the initial inputting view).
Any ideas?
EDIT: note that "a" refers to a session that includes a drop down box of all the available instances. This carried forward will indicate which instance the user wants to edit.
`
Why not do a database lookup of the model your trying to save and pull the fields from the form to the model then save the model?
Instead to store model a in session you should store it on database. Then edit it:
def edit(request, pk):
a = A.objects.get( pk = pk)
...
pk it the a identifier, you can send it to view via urls.py. I encourage to you to use POST/Redirect/GET pattern.
You can add a 'state' field on your model to control workflow (draft, valid)
You should not save objects in the session. If you really need to use a session - save a PK there and retrieve object right before giving it to Form. But the better solution is to send it in GET or POST parameters or included in url. Sessions are unreliable, data inside it can be destroyed between user's requests.
And you can retrieve value from a session in a more pythonic way:
try:
a = request.session['a']
except KeyError:
raise Http404('a was not found')

Django Form Error Handling: Wrong url? is_valid()

Hi Stackoverflow people,
I am irritated by the the Django Form handling, if the form submits to new page and the form validation fails. I intended to return to the earlier submitted form, and display the error message for correction.
The error message will be displayed, but the url link is not changing.
How can I change the *else statement of the is_valid() statement*, in order to redirect to the earlier form?
Thank you for your advice!
urls.py
urlpatterns = patterns("",
url(r"^add/$", "remap.views.add_project", name="add_project_location"),
url(r"^add/details/$", "remap.views.add_project_details", name="add_project_details"),
)
views.py
def add_project_details(request):
if request.method == 'POST': # If the form has been submitted...
locationForm = LocationForm(request.POST)
if locationForm.is_valid(): # All validation rules pass
locality = locationForm.cleaned_data['locality']
state = locationForm.cleaned_data['state']
country = locationForm.cleaned_data['country']
projectForm = ProjectForm(initial = {
'locality': locality,
'state': state,
'country': country,
})
return render_to_response('map/add_project_details.html', {
'projectForm': projectForm,
}, context_instance = RequestContext(request))
else:
print locationForm.errors
return render_to_response('map/add_project_location.html', {
'locationForm': locationForm,
}, context_instance = RequestContext(request))
else:
return HttpResponseRedirect('../') # Redirect to the location reg if site is requested w/o proper location
There's no cause to get irritated about Django doing exactly what it is designed to do.
Your redirect is on the else clause corresponding to if request.method == 'POST'. So, it will only take effect if this is not a form submission. In other words, that view will never actually display an empty form.
Then, depending on whether or not the POSTed form is valid, you display one of two templates. You're not doing any redirecting at this point.
However, even if you had asked Django to redirect when the form is invalid, I doubt you would get the result you want. This is because the rules of HTTP (not Django) say that you can't redirect a POST - so your request to the redirected view would be a GET, without any of the invalid data or error messages.
You can't update the url unless you tell the browser to do a redirect. You are returning two different html versions on the same url which can be confusing to the user.
What you should do instead is have two views. The /add url and add_project_location view would accept a post and validate, staying on that url and view until success.
Upon success it would set some session variables, redirect to the add_project_details page. Merging them like this is just not possible because of how browsers work.

django - passing information when redirecting after POST

I have a simple form, that when submitted redirects to a success page.
I want to be able to use the data that was submitted in the previous step, in my success page.
As far as I know, you can't pass POST data when redirecting, so how do you achieve this?
At the moment I'm having to just directly return the success page from the same URL, but this causes the dreaded resubmission of data when refreshed.
Is using request.session the only way to go?
I do this all the time, no need for a session object. It is a very common pattern POST-redirect-GET. Typically what I do is:
Have a view with object list and a form to post data
Posting successfully to that form saves the data and generates a redirect to the object detail view
This way you save upon POST and redirect after saving.
An example view, assuming a model of thingies:
def all_thingies(request, **kwargs):
if request.POST:
form = ThingieForm(request.POST)
if form.is_valid():
thingie = form.save()
return HttpResponseRedirect(thingie.get_absolute_url())
else:
form = ThingieForm()
return object_list(request,
queryset = Thingie.objects.all().order_by('-id'),
template_name = 'app/thingie-list.html',
extra_context = { 'form': form },
paginate_by = 10)
You can:
Pass the data (either full data or just id to object) in request.session
Redirect with something like ?id=[id] in URL - where [id] points to your object.
Update:
Regarding pt. 1 above, I meant that you could do (in POST handler):
my_object = MyModel.objects.create(...)
request.session['my_object_id'] = my_object.id
Or you could try passing the whole object (it should work but I'm not 100% certain):
my_object = MyModel.objects.create(...)
request.session['my_object'] = my_object

How to write good form validation in Django?

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.