Where does form.is_valid() clean the form?

748 views
Skip to first unread message

Jacob G

unread,
Sep 20, 2011, 7:49:39 PM9/20/11
to django...@googlegroups.com
The Django documentation says that a form is cleaned when calling is_valid():

However, the is_valid soure code doesn't show that is does clean, as follows:

def is_valid(self):
"""
Returns True if the form has no errors. Otherwise, False. If errors are
being ignored, returns False.
"""
return self.is_bound and not bool(self.errors)

And empirically in my application, I don't see any cleaning taking place when I call is_valid.

Am I understanding something wrong? Is an application supposed to full_clean() directly followed by a call to is_valid()?

Thanks.
 
 
 

Shawn Milochik

unread,
Sep 20, 2011, 8:11:40 PM9/20/11
to django...@googlegroups.com
Take your study/research one step further:

https://code.djangoproject.com/browser/django/trunk/django/forms/forms.py

When it checks for self.errors, the _get_errors() function calls the full_clean.

Jacob G

unread,
Sep 20, 2011, 8:14:00 PM9/20/11
to Django users
Thanks, I didn't realize errors is property that calls _get_errors().

On Sep 20, 4:11 pm, Shawn Milochik <sh...@milochik.com> wrote:
> Take your study/research one step further:
>
> https://code.djangoproject.com/browser/django/trunk/django/forms/form...

Shawn Milochik

unread,
Sep 20, 2011, 8:17:52 PM9/20/11
to django...@googlegroups.com
On Tue, Sep 20, 2011 at 4:14 PM, Jacob G <ja...@fareclock.com> wrote:
> Thanks, I didn't realize errors is property that calls _get_errors().
>

Yeah, it's non-obvious. That's one of the nice things about
open-source, though. You can go through the code and understand how
your tools work so you can better apply them.

Side-note -- now that you're browsing Django's code, I think you could
easily participate by going to the Trac instance and closing a few
easy tickets. ;o) https://code.djangoproject.com/

Shawn

Jacob G

unread,
Sep 20, 2011, 8:46:53 PM9/20/11
to Django users
I doubt I'm familiar enough with Django to fix bugs.

I am puzzled by what I am see though with the BaseForm class in Aptana
PyDev debugger. I noticed that full_clean() was not being called via a
call to is_valid. So I stepped through the debugger, and for some
strange reason, the BaseForm constructor, although the code says
"self._errors = None" the debugger watchlist shows self._errors as set
to an empty ErrorDict, and therefore the _get_errors() method never
called full_clean().

Does this make any sense? It doesn't sound right.

Shawn Milochik

unread,
Sep 20, 2011, 8:55:42 PM9/20/11
to django...@googlegroups.com
On Tue, Sep 20, 2011 at 4:46 PM, Jacob G <ja...@fareclock.com> wrote:
> I doubt I'm familiar enough with Django to fix bugs.
>
Wait until the talks from DjangoCon US 2011 are out, then watch my
lightning talk. I guarantee you could submit at least three patches
today alone.

> I am puzzled by what I am see though with the BaseForm class in Aptana
> PyDev debugger. I noticed that full_clean() was not being called via a
> call to is_valid. So I stepped through the debugger, and for some
> strange reason, the BaseForm constructor, although the code says
> "self._errors = None" the debugger watchlist shows self._errors as set
> to an empty ErrorDict, and therefore the _get_errors() method never
> called full_clean().
>
> Does this make any sense? It doesn't sound right.
>

I haven't dug into this particular code, so I don't know why it works
that way. In any case, what is it that you're actually trying to
accomplish? I ask because I'm pretty sure Django form validation isn't
buggy or we'd know by now. If you're just trying to understand Django
then that's awesome, and I hope you figure it out or someone helps
out. But if you're digging into it to try to solve a problem in your
own code, what is it?

Shawn

Jacob G

unread,
Sep 20, 2011, 9:02:38 PM9/20/11
to Django users
I'm doing this because there's a problem in my code. I create a
AuthenticationForm, and post a non-existing username and password, and
call is_valid(). I expect to then that the errors property will return
at least one error, but there are actually no errors. So I step
through the debugger, and see that full_clean() is not getting called,
because _errors apparently is never None, even after setting it to
None in the BaseForm constructor.

I agree with you that I'm sure such a bug doesn't exist. But I can't
understand at all why it looks like this in the debugger. How can the
code set a variable to None, and it is set to an empty ErrorDict? It
seems like a practical joke or something.

Shawn Milochik

unread,
Sep 20, 2011, 9:18:33 PM9/20/11
to django...@googlegroups.com
I think this can be easily explained by pointing out a misunderstanding.

Whether or not a form's values are valid has nothing to do with
whether the values are meaningful. That is, a login form with a
present but incorrect password is in fact valid.

I'm sure that full_clean is getting called, and the default value of
None is getting replaced by an empty ErrorDict once the clean
determines that no fields are invalid.

If you look at the code that's used by the django.contrib.auth to log
users in, there are more steps after simple form validation needed to
log a user in.
https://docs.djangoproject.com/en/1.3/topics/auth/#authentication-in-web-requests

After the form is validated, the values are then used with the
authenticate method to attempt to find a matching user in the
database. If one is found, an additional step is taken to update the
request with the user.

Understanding that, plus checking out the link above should clear up
your confusion. If you read and understand it but still think your
code has a different problem, please describe it in more detail.

Shawn

James Pyrich

unread,
Sep 20, 2011, 9:04:44 PM9/20/11
to django...@googlegroups.com
My guess, for whatever it's worth, is that the debugger is evaluating
the errors property before the form is bound, causing self._errors to be
populated with an empty ErrorDict. If so, the state revealed by the
debugger is not an accurate reflection of the code as it would normally
execute.

Jacob G

unread,
Sep 21, 2011, 2:33:06 PM9/21/11
to django...@googlegroups.com
Thanks all.

I figured out my mess-up: In the view, I instantiated the form incorrectly as follows:
form = AuthenticationForm(request.POST)
Instead of the correct way:
form = AuthenticationForm(request, data=request.POST)

The debugger weirdness distracted me from the real problem. I still don't know what happened with the debugger, but I've got other things to do, so I'm moving on...

Reply all
Reply to author
Forward
0 new messages