Forms hierarchy issue in Django - django

I created a form for login, just like this:
class LoginForm(AuthenticationForm):
username = forms.CharField (label=_("Usuario"), max_length=30,
widget=forms.widgets.
TextInput(attrs={'id':'username','maxlength':'25'}))
password = forms.CharField (label=_("Password"), widget=forms.widgets.
PasswordInput(attrs={'id':'password','maxlength':'10'}))
I use it in this view:
def login(request):
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
...
After debugging I realize that the form.is_valid() method returns false cause the is_bound attr is false. Do I have to redefine something in my form or to modify my view???
Edit 1
I have followed this SO question about is_valid() method returning False:
form.is_valid() always returning false
but the problem is still there.

One issue to note about the currently accepted answer:
form = LoginForm(request=request, data=request.POST)
is that passing in request seems to enable Django's behavior of checking to see if a test cookie was successful before initiating a session. The problem is that if you haven't set the test cookie previously (it has to be set in a previous view request) it will fail and your login will fail. I recommend just passing the data keyword argument in like so:
form = LoginForm(data=request.POST)
Unless I'm missing something important (it didn't seem like the cookie check is absolutely necessary), this works better in most situations. You could alternatively call request.set_test_cookie() in the view that loads the login page, but that doesn't cover all scenarios.

The issue is actually similar to the one in the question you link to. The form you're inheriting from, django.contrib.auth.forms.AuthenticationForm, takes request as its first parameter, before the usual data param. This is why your form is reporting that it is not bound - as far as it's concerned, you're not passing in any data, so it has nothing to bind to.
So, in your view, you'll need to instantiate it like this:
form = LoginForm(request=request, data=request.POST)

Related

Seemingly weird logic of HTTP requests in Django

I have a problem that I solved but the fact that I don't understand how tells me that there is something basic I'm missing. Hope somebody can help me.
So I have a class based update view with inline formsets. I use crispy forms to render the view. The code below was first working, then for some reason it started giving me Management form missing error when just trying to load the update page in my browser.
#views.py:
class CaveUpdateView(UpdateView):
model=Cave
form_class=CaveForm
template_name='caves/cave_form.html'
def get_context_data(self,**kwargs):
context = super(CaveUpdateView, self).get_context_data(**kwargs)
entrance_helper = EntranceFormSetHelper()
context['entrance_helper'] = entrance_helper
if self.request.GET:
context['entrance_formset']=EntranceInlineFormSet(instance=self.object)
else:
context['entrance_formset']=EntranceInlineFormSet(self.request.POST, instance=self.object)
After wrecking my brains out, I changed the last 4 lines of the get_context_data function to this and everything was solved:
if self.request.POST:
context['entrance_formset']=EntranceInlineFormSet(self.request.POST, instance=self.object)
else:
context['entrance_formset']=EntranceInlineFormSet(instance=self.object)
So my question is, how are these two expressions not equivalent? Is there another type of request I somehow make my browser send by refreshing?
if self.request.GET doesn't mean "if the request is a GET" - and if self.request.POST doesn't mean "if the request is a POST". They are using boolean operators on the GET and POST dictionaries respectively - and in Python, dicts are boolean False if they're empty and True otherwise.
So, your calls are actually asking "does the request have some querystring parameters", which may or may not be true whether or not the request is a GET, and "does the request have a body", which will not be true with an empty POST.
If you actually want to check the type of the request,you should explicitly check if request.method == 'GET' (or 'POST').

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

Prevent Django forms highlighting correct fields

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.

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.

Checking for content in Django request.POST

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