newforms unique field(s)

10 views
Skip to first unread message

Matt

unread,
May 21, 2007, 12:10:41 PM5/21/07
to Django users
I have a form that allows users to submit their name and email address
for an invitation.
I do not want people signing up more than once. The following code
works, but it does not seem like the "Best Practice" solution.

forms.py
[...imports...]
class RequestInvite(forms.Form):
name = forms.CharField(max_length=75)
email = forms.EmailField()

def clean_name(self):
try:
u = UserInvite.objects.get(name__contains =
self.clean_data['name'])
raise ValidationError('You have already signed up.')
except UserInvite.DoesNotExist:
return self.clean_data['name']

def clean_email(self):
try:
u = UserInvite.objects.get(email__contains =
self.clean_data['email'])
raise ValidationError('You have already signed up.')
except UserInvite.DoesNotExist:
return self.clean_data['email']

def save(self):
invite = UserInvite(
name = self.clean_data['name'],
email = self.clean_data['email'],)
invite.save()

views.py
[...imports...]
def request_invite(request):

f = RequestInvite()

if request.method == 'POST':
invite_me = RequestInvite(request.POST)
if invite_me.is_valid():
invite_me.save()
msg = "We will be sending out invitations shortly."
return render_to_response( 'invite/thanks.html',
{'msg': msg})
else:
f = invite_me

return render_to_response( 'invite/index.html',
{'form':f,})

If someone could point me in the right direction it would be greatly
appreciated.
Thanks Matt

Joseph Heck

unread,
May 21, 2007, 12:30:18 PM5/21/07
to django...@googlegroups.com
What is the "general way" to add your own validation to forms under
the newforms framework?

Subclassing and adding in your clean_* methods? Using decorators or
the such around existing validation? Adding your own custom Field
objects and setting their "clean" methods?

-joe

Tim Chase

unread,
May 21, 2007, 3:00:21 PM5/21/07
to django...@googlegroups.com
> What is the "general way" to add your own validation to forms under
> the newforms framework?
>
> Subclassing and adding in your clean_* methods? Using decorators or
> the such around existing validation? Adding your own custom Field
> objects and setting their "clean" methods?

My understanding (easily wrong, and appreciating correction if
so) is that they are divergent concepts, each of which serves its
own purpose.

The clean_* methods are used for *cleaning* the data. Thus, you
might have a textbox for a phone number. Cleaning that might
involve stripping it down to purely the number. Thus, the user
could enter "(800)555-1212" or "800-555-1212" or "800.555.1212"
or perhaps even "800-DJANGO1". These should be "cleaned" to
their normalized representation of "8005551212" (or
"8003526461"). Likewise, one might have a monitary field where
the user might type something like "$3,141.59" in the field and
you really want to clean it to just "3141.59".

The validators associated with the model should then ensure that
this cleaned value is also a valid value. Perhaps you don't
allow phone numbers with a "900" area code (usually
pay-through-the-nose costs-per-minute associated with them).
Perhaps you want to ensure that the phone number is "local" as
defined by a set of given area-codes. Or perhaps your money
field is intended to be a cost, so you want to validate that it's
not a negative number.

However, it's also my understanding that the clean_* methods
should also catch any validator errors. I don't know if this
goes on behind the covers, or if it happens automatically.
However, that's what I undertand the distinction to be. Perhaps
one of the framework gurus could shed light on "the way it should
be done"...

-tim


Matt

unread,
May 21, 2007, 3:57:55 PM5/21/07
to Django users
After looking at http://code.google.com/p/django-registration/:

forms.py
[...]
def clean_username(self):
"""
Validates that the username is not already in use.

"""
if self.cleaned_data.get('username', None):
try:
user =
User.objects.get(username__exact=self.cleaned_data['username'])
except User.DoesNotExist:
return self.cleaned_data['username']
raise forms.ValidationError(u'This username is already
taken. Please choose another.')
[...]

It seems like the right way to go... but as Tim said, it would be nice
if the framework gurus could shed some light on the subject.

-m

Malcolm Tredinnick

unread,
May 21, 2007, 6:24:24 PM5/21/07
to django...@googlegroups.com
On Tue, 2007-05-22 at 08:11 +1000, Malcolm Tredinnick wrote:

> On Mon, 2007-05-21 at 14:00 -0500, Tim Chase wrote:
> > > What is the "general way" to add your own validation to forms under
> > > the newforms framework?
> > >
> > > Subclassing and adding in your clean_* methods? Using decorators or
> > > the such around existing validation? Adding your own custom Field
> > > objects and setting their "clean" methods?
> >
> > My understanding (easily wrong, and appreciating correction if
> > so) is that they are divergent concepts, each of which serves its
> > own purpose.
> [...]

>
> > Perhaps
> > one of the framework gurus could shed light on "the way it should
> > be done"...
>
> Tim's explanation is mostly on the money. Since I have a "framework
> guru" T-shirt around here somewhere (actually, I don't -- I want to get
> a Django T-shirt one day), here's my understanding.
>
> There are three types of cleaning methods that are run during form
> processing. These are all executed when you access Form.errors or call
> call Form.full_clean() explicitly (or Form.is_valid(), which accesses
> Form.errors).
>
> Any cleaning method can raise ValidationError if there is a problem with
> the data it is processing, passing the relevant error message to the
> ValidationError's constructor. If no ValidationError is raised, the
> method should return a Python object for the cleaned (normalised) data.
>
> The three types of methods are:
>
> (1) The clean() method on a Field subclass. This is responsible
> for cleaning the data in a way that is generic for that type of
> field. For example, a FloatField will turn the data into a
> Python float or raise a ValidationError.
>
> (2) The clean_fieldname() method -- where "fieldname" is
> replaced with the name of the form field attribute. This method
> does any cleaning that is specific to that particular attribute,
> unrelated to the type of field that it is. In Matt's original
> problem, clean_username() would be the right place to do any
> uniqueness validation. You don't need a specific Username field
> -- it's just a CharField, really -- but you want a
> formfield-specific piece of validation and, possibly, cleaning.

Something I should have made clear, here (I think the people replying to
this thread already understand this, but I'm trying to be
comprehensive):

The clean_<fieldname> method is a method on your Form subclass, not on
any particular field. So you would write a form that looks like:

class MyForm(forms.Forms):
username = forms.CharField(...)

def clean_username(self):
....
return nice_shiny_clean_username_string

Regards,
Malcolm


Malcolm Tredinnick

unread,
May 21, 2007, 6:30:20 PM5/21/07
to django...@googlegroups.com
[Apologies if this appears twice. I think Google ate my homework the
first time.]

On Mon, 2007-05-21 at 14:00 -0500, Tim Chase wrote:

> > What is the "general way" to add your own validation to forms under
> > the newforms framework?
> >
> > Subclassing and adding in your clean_* methods? Using decorators or
> > the such around existing validation? Adding your own custom Field
> > objects and setting their "clean" methods?
>
> My understanding (easily wrong, and appreciating correction if
> so) is that they are divergent concepts, each of which serves its
> own purpose.

[...]

> Perhaps
> one of the framework gurus could shed light on "the way it should
> be done"...

Tim's explanation is mostly on the money. Since I have a "framework


guru" T-shirt around here somewhere (actually, I don't -- I want to get
a Django T-shirt one day), here's my understanding.

There are three types of cleaning methods that are run during form
processing. These are all executed when you access Form.errors or call
call Form.full_clean() explicitly (or Form.is_valid(), which accesses
Form.errors).

Any cleaning method can raise ValidationError if there is a problem with
the data it is processing, passing the relevant error message to the
ValidationError's constructor. If no ValidationError is raised, the
method should return a Python object for the cleaned (normalised) data.

The three types of methods are:

(1) The clean() method on a Field subclass. This is responsible
for cleaning the data in a way that is generic for that type of
field. For example, a FloatField will turn the data into a
Python float or raise a ValidationError.

(2) The clean_fieldname() method -- where "fieldname" is
replaced with the name of the form field attribute. This method
does any cleaning that is specific to that particular attribute,
unrelated to the type of field that it is. In Matt's original
problem, clean_username() would be the right place to do any
uniqueness validation. You don't need a specific Username field
-- it's just a CharField, really -- but you want a
formfield-specific piece of validation and, possibly, cleaning.

(3) The Form subclass's clean() method. This method can perform
any validation that requires access to multiple fields from the
form at once. This is where you might put in things to check
that if fieldA is supplied, fieldB must contain a valid email
address and the like. The data that this method returns is the
final cleaned_data attribute for the form, so don't forget to
return the full list of cleaned data if you override this method
(by default, Form.clean() just returns self.cleaned_data).

Note that any errors raised by your Form.clean() override will
not be associated with any field in particular. They go into a
special "field" called "__all__", which you can access via
Form.non_field_errors() if you need to. You don't need to care
about __all__ at all (ha, ha!).

These methods are run in the order given above, one field at a time.
That is, for each field in the form (in the order they are declared in
the form definition), the Field.clean() method (or it's override) is
run, then clean_<fieldname>(). Finally, once those two methods are run
for every field, the Form.clean() method, or it's override, is executed.

Again, any of these methods can raise a ValidationError. For any field,
if step (1) raises a ValidationError, step (2) is not run, however,
steps (1) and (2) are run for all remaining fields. Regardless of the
results of steps (1) and (2), step (3) is always run. If step (3) raises
a ValidationError, self.cleaned_data will be an empty dictionary.

The previous paragraph means that if you are overriding Form.clean(),
you should iterate through self.cleaned_data.items(), rather than
through the list of fields in the forms: not every field may end up
contributing to cleaned_data, because they may have raised
ValidationErrors themselves, so don't assume you have the data you need
by accessing the form fields directly -- always talk to
self.cleaned_data.

Hopefully that clears up some of the mystery around the various form
validation phases. If somebody wants to write that up as a patch to the
newforms documentation, go crazy.

Regards,
Malcolm


Malcolm Tredinnick

unread,
May 21, 2007, 6:11:17 PM5/21/07
to django...@googlegroups.com
On Mon, 2007-05-21 at 14:00 -0500, Tim Chase wrote:
> > What is the "general way" to add your own validation to forms under
> > the newforms framework?
> >
> > Subclassing and adding in your clean_* methods? Using decorators or
> > the such around existing validation? Adding your own custom Field
> > objects and setting their "clean" methods?
>
> My understanding (easily wrong, and appreciating correction if
> so) is that they are divergent concepts, each of which serves its
> own purpose.
[...]

> Perhaps
> one of the framework gurus could shed light on "the way it should
> be done"...

Tim's explanation is mostly on the money. Since I have a "framework

simonbun

unread,
May 22, 2007, 1:04:59 AM5/22/07
to Django users
Great stuff again Malcolm. Perhaps it should be rehashed a bit and put
in the newforms docs under 'custom validation' ?

Just a thought :)

regards,
Simon

On May 22, 12:11 am, Malcolm Tredinnick <malc...@pointy-stick.com>
wrote:

Joseph Heck

unread,
May 22, 2007, 12:47:46 PM5/22/07
to django...@googlegroups.com
I thought the same thing. :-) I'll take a whack at wringing it
together and submitting it as a patch, and you can be appalled at my
slaughtering of your fine explanation.

-joe

josep...@gmail.com

unread,
Jun 4, 2007, 1:28:26 PM6/4/07
to Django users
I finally pulled this together and submitted it as a ticket:
http://code.djangoproject.com/ticket/4473

-joe

On May 22, 9:47 am, "Joseph Heck" <joseph.h...@gmail.com> wrote:
> I thought the same thing. :-) I'll take a whack at wringing it
> together and submitting it as a patch, and you can be appalled at my
> slaughtering of your fine explanation.
>
> -joe
>

> On 5/21/07, simonbun <simonce...@gmail.com> wrote:
>
>
>
> > Great stuff again Malcolm. Perhaps it should be rehashed a bit and put

> > in thenewformsdocs under 'customvalidation' ?


>
> > Just a thought :)
>
> > regards,
> > Simon
>
> > On May 22, 12:11 am, Malcolm Tredinnick <malc...@pointy-stick.com>
> > wrote:
> > > On Mon, 2007-05-21 at 14:00 -0500, Tim Chase wrote:
> > > > > What is the "general way" to add your ownvalidationto forms under
> > > > > thenewformsframework?
>
> > > > > Subclassing and adding in your clean_* methods? Using decorators or

> > > > > the such around existingvalidation? Adding your own custom Field

> > > uniquenessvalidation. You don't need a specific Username field


> > > -- it's just a CharField, really -- but you want a

> > > formfield-specific piece ofvalidationand, possibly, cleaning.


>
> > > (3) The Form subclass's clean() method. This method can perform

> > > anyvalidationthat requires access to multiple fields from the

> > >validationphases. If somebody wants to write that up as a patch to the

Reply all
Reply to author
Forward
0 new messages