Django form.is_valid() not calling method clean_<fieldname> - django

I copied a simple example of a user registration page in Django 1.0 Web Site Development. I have defined the following form:
class RegistrationForm(forms.Form):
username = forms.CharField(label=u'Username', max_length=30)
email = forms.EmailField(label=u'Email')
password1 = forms.CharField(
label=u'Password',
widget=forms.PasswordInput()
)
password2 = forms.CharField(
label=u'Password (Again)',
widget=forms.PasswordInput()
)
def clean_password2(self):
if 'password1' in self.cleaned_data:
password1 = self.cleaned_data['password1']
password2 = self.cleaned_data['password2']
if password1 == password2:
return password2
raise forms.ValidationError('Passwords do not match.')
def clean_username(self):
print "Validating username..."
username = self.cleaned_data['username']
if not re.search(r'^\w+', username):
raise forms.ValidationError('Username can only contain '
'alphanumeric characters and the underscore.')
try:
User.objects.get(username=username)
except User.DoesNotExist:
return username
raise forms.ValidationError('Username is already taken')
In the case where a username is already taken, the field is validated using the normal form validation and my custom clean method is not called. When the form is submitted to register_page, form.is_valid() returns True.
Is there something missing that needs to be done in order for Django to know to call the clean_ methods?
def register_page(request):
if request.method == 'POST':
print "Posted to registration form"
form = RegistrationForm(request.POST)
if form.is_valid():
user = User.objects.create_user(
username=form.cleaned_data['username'],
password=form.cleaned_data['password1'],
email=form.cleaned_data['email']
)
return HttpResponseRedirect('/')
else:
form = RegistrationForm()
return render_to_response(
'users/registration.html',
context_instance=RequestContext(request, {
'form' : form
})

In case anybody else gets here via google and the above answer doesn't help:
I had this same problem and it turned out to be I was not setting exclude as a tuple in my forms meta:
class MyFormClass(ModelForm):
class Meta:
exclude = ('type_detail') #wrong
instead of:
class MyFormClass(ModelForm):
class Meta:
exclude = ('type_detail', ) #right

This was truly puzzling. I restored a auto-save version of forms.py and it magically started working, yet there is no difference that I can discern between the code snippet above, and the restored file.
Short answer: "Programmer Error"

Related

Extending form.is_valid()

I am learning Django, and i stumbled upon something that I need help with:
forms.py
class UserForm(forms.ModelForm):
password1 = forms.CharField(widget=forms.PasswordInput())
password2 = forms.CharField(widget=forms.PasswordInput())
class Meta:
model = User
fields = ('username', 'email', 'password1','password2')
def password_matched(self):
if self.data['password1'] != self.data['password2']:
self.errors['password'] = 'Passwords do not match'
return False
else:
return True
def is_valid(self):
valid = super(UserForm,self).is_valid()
password_matched = self.password_matched()
if valid and password_matched:
return True
else:
return False
views.py
def register(request):
#blah...
user.set_password(user.password)
# user.set_password(user.password1) doesn't work ! WHY!?
So basically, I am checking if pw1 == pw2,
after checking, I wish to set the user's password to password1.
I initially used the line user.set_password(user.password1) but it complained that User object doesn't have password1, yet it worked when I used password.
Why is that? Thanks.
You should be ideally using the clean method for this, and never be touching the is_valid method.
Something like this:
def clean(self):
cd = self.cleaned_data
password1 = cd.get("password1")
password2 = cd.get("password2")
if password1 != password2:
#Or you might want to tie this validation to the password1 field
raise ValidationError("Passwords did not match")
return cd
Now, in the views,
def register(request):
#blah...
form = UserForm(request.POST or None)
if request.method == "POST":
if form.is_valid(): #This would call the clean method for you
user = User.objects.create(...)
user.set_password(form.cleaned_data.get("password1"))
user.save()
else: #Form is invalid
print form.errors #You have the error list here.

How I can validate fields on forms on identity fields in form. Django

How I can validate fields on forms on identity fields in form instantly without POST request?
my fields in model:
password = models.CharField(max_length=45, verbose_name="")
password2 = models.CharField(max_length=45, verbose_name="")
and my fields in form:
'password': widgets.PasswordInput(attrs={'placeholder': 'New Password'}),
'password2': widgets.PasswordInput(attrs={'placeholder': 'Re-Entere Password'}),
This is the code I tried:
def clean_password(self):
password1 = self.cleaned_data.get('password')
password2 = self.cleaned_data.get('password2')
if not password2:
raise forms.ValidationError("You must confirm your password")
if password1 != password2:
raise forms.ValidationError("Your passwords do not match")
return password2
Code in view:
def saves_data_user_on_registration (request):
if request.method == 'POST':
c = {}
c.update(csrf(request))
form_user_data = Form_registration(request.POST, request.FILES)
if form_user_data.is_valid():
print form_user_data.errors
form_user_data.save()
return render_to_response('see_you_later.html', c, context_instance=RequestContext(request))
else:
print form_user_data.errors
return render_to_response('error.html', c, context_instance=RequestContext(request))
I you want to access two fields during validation you need to use clean() not clean_myfield().
Check the docs.
What do you mean with "without a POST request?".
I would never accept passwords via GET. HTTP GET requests get stored in proxies and caches!

Django user registration validation not working

Hello i want to develop validation to user registration if user exists and is password matches the password confirmation field. Unfortunately, validation doesn't works. For example, if the 2 passwords don't match, the registration completes anyway with the first password. I want, if there is a problem to reload the registration form with the problems highlighted.
the form:
class RegistrationForm(forms.Form):
username = forms.CharField(label=u'Username', max_length=30)
first_name = forms.CharField(label=u'First Name', max_length=30)
last_name = forms.CharField(label=u'Last Name', max_length=30)
email = forms.EmailField(label=u'Email')
password1 = forms.CharField(
label=u'Password',
widget=forms.PasswordInput()
)
password2 = forms.CharField(
label=u'Password (Again)',
widget=forms.PasswordInput()
)
def clean_password2(self):
if 'password1' in self.cleaned_data:
password1 = self.cleaned_data['password1']
password2 = self.cleaned_data['password2']
if password1 == password2:
return password2
raise forms.ValidationError('Passwords do not match.')
def clean_username(self):
username = self.cleaned_data['username']
if not re.search(r'^\w+$', username):
raise forms.ValidationError('Username can only contain '
'alphanumeric characters and the underscore.')
try:
User.objects.get(username=username)
except User.DoesNotExist:
return username
raise forms.ValidationError('Username is already taken.')
the view:
def register_page(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
user = User.objects.create_user(
username=form.cleaned_data['username'],
password=form.cleaned_data['password1'],
email=form.cleaned_data['email']
)
UserProfile.first_name=form.cleaned_data['first_name']
created = UserProfile.objects.get_or_create(
user_id=user.id, first_name=form.cleaned_data['first_name'], last_name=form.cleaned_data['last_name'] )
return HttpResponseRedirect('/register/success/')
else:
form = RegistrationForm()
variables = RequestContext(request, {
'form': form
})
return render_to_response(
'registration/register.html', variables)
class RegistrationForm(forms.Form):
//fields
def clean(self):
cleaned_data = super(RegistrationForm, self).clean()
username = cleaned_data.get("username")
password1 = cleaned_data.get("password1")
password1 = cleaned_data.get("password1")
#validate username
user = User.objects.filter(username=username)
if user:
raise forms.ValidationError(
"That user is already taken , please select another ")
elif not re.search(r'^\w+$', username):
raise forms.ValidationError(
"Username can only contain"
"alphanumeric characters and the underscore.")
#validate password
if password1 != password1:
raise forms.ValidationError(
"Your current and confirm password do not match.")
return cleaned_data
I use this simple validation which works.
def validate(self, value):
data = self.get_initial()
username = data.get("username")
email = data.get("email")
password = data.get("password")
confirm_password = data.get("confirm_password")
max_similarity = 0.7
user_qs = User.objects.filter(email=username)
if user_qs.exists():
raise ValidationError("Username already exist")
if(password != confirm_password):
raise ValidationError("Password and Confirm password does not match")
if SequenceMatcher(a=password.lower(), b=username.lower()).quick_ratio() > max_similarity:
raise serializers.ValidationError("The password is too similar to the username.")
if SequenceMatcher(a=password.lower(), b=email.lower()).quick_ratio() > max_similarity:
raise serializers.ValidationError("The password is too similar to the email.")
return data
Additional validation:
Also you can add some default validation of django by adding this.
This will check minimum length of the password and max string and max integer.
def validate_password(self, value):
try:
validate_password(value)
except ValidationError as exc:
raise serializers.ValidationError(str(exc))
return value

Django KeyError at /register/

I have a registration that let users register and i'm having difficulty fixing it.
The problem is when a user submits a single field instead of the whole form for example an email . I get this error
KeyError at /register/
password
Request Method: POST
Request URL: http://127.0.0.1:8000/register/
File "C:\Python26\Lib\site-packages\django\forms\forms.py" in _get_errors
115. self.full_clean()
File "C:\Python26\Lib\site-packages\django\forms\forms.py" in full_clean
271. self._clean_form()
File "C:\Python26\Lib\site-packages\django\forms\forms.py" in _clean_form
299. self.cleaned_data = self.clean()
File "C:\o\17\mysite\pet\forms.py" in clean
31. if self.cleaned_data['password'] != self.cleaned_data['password1']:
Exception Type: KeyError at /register/
Exception Value: password
I tried to fix this solution using if . If user has a submitted a username or any other required field , process the form otherwise redisplay the original form.
but I still get the same error.
This is my edited views.py (at the bottom of the page is my original RegistrationForm)
def PetRegistration(request):
if request.user.is_authenticated():
return HttpResponseRedirect(reverse('world:HappyLand'))
if request.method =='POST':
form = UserRegistration(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
if username:
email=form.cleaned_data['email']
if email:
password=form.cleaned_data['password']
if password:
user = User.objects.create_user(
username=form.cleaned_data['username'],
email=form.cleaned_data['email'],
password=form.cleaned_data['password']
)
user.is_active = True
user.first_name = form.cleaned_data['name']
user.save()
person = authenticate(
username=form.cleaned_data['username'],
password=form.cleaned_data['password']
)
Person.objects.create(user_id=user.id,
name=form.cleaned_data['name'],birthday=form.cleaned_data['birthday'])
login(request, person)
return HttpResponseRedirect(reverse('world:HappyLand'))
return render(request, 'register.html', {'form': UserRegistration()})
How can I fix this error and also how could I display an error message on the other fields that the user didn't fill out like "Error Missing Field , Please Fill this Field".
def PetRegistration(request):
if request.user.is_authenticated():
return HttpResponseRedirect(reverse('world:HappyLand'))
if request.method =='POST':
form = UserRegistration(request.POST)
if form.is_valid():
user = User.objects.create_user(
username=form.cleaned_data['username'],
email=form.cleaned_data['email'],
password=form.cleaned_data['password']
)
user.is_active = True
user.first_name = form.cleaned_data['name']
user.save()
person = authenticate(
username=form.cleaned_data['username'],
password=form.cleaned_data['password']
)
Person.objects.create(user_id=user.id,
name=form.cleaned_data['name'],birthday=form.cleaned_data['birthday'])
login(request, person)
return HttpResponseRedirect(reverse('world:HappyLand'))
return render(request, 'register.html', {'form': UserRegistration()})
My forms.py
class UserRegistration(forms.Form):
username = forms.CharField()
name = forms.CharField()
email = forms.EmailField()
birthday = forms.DateField(widget=extras.SelectDateWidget(years=range(1950, 2012)))
password = forms.CharField(
widget=forms.PasswordInput(render_value=False)
)
password1 = forms.CharField(
label=(u'Verify Password'),
widget = forms.PasswordInput(render_value=False)
)
def clean_username(self):
username = self.cleaned_data['username']
try:
User.objects.get(username=username)
except User.DoesNotExist:
return username
raise forms.ValidationError(
"That user is already taken , please select another ")
def clean(self):
if self.cleaned_data['password'] != self.cleaned_data['password1']:
raise forms.ValidationError("The password does not match ")
return self.cleaned_data
My models.py
class Person(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=100, blank=True)
birthday = models.DateField(blank=True,null=True)
def __unicode__(self):
return self.name
Problem is with your clean(). In clean(), you are trying to access field password on form's cleaned_data. password will only be available on cleaned_data if the user has filled this field. So, you must check that password is there in cleaned_data before trying to access it.
Changing your clean():
def clean(self):
if 'password' in self.cleaned_data and 'password1' in self.cleaned_data and self.cleaned_data['password'] != self.cleaned_data['password1']:
raise forms.ValidationError("The password does not match ")
return self.cleaned_data
You can provide a keyword argument error_messages on form field for showing error message like "Error Missing Field , Please Fill this Field".
class SomeForm(forms.Form):
name = forms.CharField(error_messages={'required':'Error Missing Field , Please Fill this Field'})
There is a bug in your view.
is_valid() populates errors on the form but this same form instance must be sent to the template so that you can access the errors on the form's fields.
But in your view, you have only one call to render() which gets called even in case of an invalid form on a post request. And in this render(), you are creating a new instance of form. So, this new form which you are sending to template will not have any errors.
So, making slight modification to your view:
def PetRegistration(request):
if request.user.is_authenticated():
return HttpResponseRedirect(reverse('world:HappyLand'))
form = UserRegistration() #This will be used in GET request
if request.method =='POST':
form = UserRegistration(request.POST) #This will be used in POST request
if form.is_valid():
user = User.objects.create_user(
username=form.cleaned_data['username'],
email=form.cleaned_data['email'],
password=form.cleaned_data['password']
)
user.is_active = True
user.first_name = form.cleaned_data['name']
user.save()
person = authenticate(
username=form.cleaned_data['username'],
password=form.cleaned_data['password']
)
Person.objects.create(user_id=user.id,
name=form.cleaned_data['name'],birthday=form.cleaned_data['birthday'])
login(request, person)
return HttpResponseRedirect(reverse('world:HappyLand'))
return render(request, 'register.html', {'form': form})
Notice in your view, I have added form=UserRegistration() before checking if its POST request, and have added the comment at two places where we are instantiating UserRegistration. And then in render(), you should send this form.
Then your {{form.username.errors}} will work.
I just modified your forms.py
class UserRegistration(forms.Form):
username = forms.CharField()
name = forms.CharField()
email = forms.EmailField()
birthday = forms.DateField(widget=extras.SelectDateWidget(years=range(1950, 2012)))
password = forms.CharField(
widget=forms.PasswordInput(render_value=False)
)
password1 = forms.CharField(
label=(u'Verify Password'),
widget = forms.PasswordInput(render_value=False)
)
def clean(self):
cleaned_data = super(UserRegistration, self).clean()
username = cleaned_data.get("username")
password = cleaned_data.get("password")
password1 = cleaned_data.get("password1")
#check if username exist
user = User.objects.filter(username=username)
if user:
raise forms.ValidationError(
"That user is already taken , please select another ")
#check password
if password != password1:
raise forms.ValidationError(
"Your current and confirm password do not match.")
return cleaned_data

Displaying error msg that the user entered the wrong password

I am trying to write a page for a user to update their information and change their password.
I have it working now. The code that I have provided works, but I do not know how to tell the user they entered the wrong password. If the user enters a password the form is valid but if they enter the wrong password I want to say the form is not valid. But if I put forms.ValidationError("some error msg") in the else stmt that right now says forms.errors, it doesn't go back to the template but displays the error msg in a compiler msg page.
This is my
from django.contrib.auth.models import User
def edit_page(request):
u = request.user
if request.method == 'POST':
form = EditUserForm(request.POST)
if form.is_valid():
if u.check_password(form.cleaned_data['oldPassword']):
u.set_password(form.cleaned_data['password1'])
u.save()
return HttpResponseRedirect('/')
else:
form.errors //Where I put forms.ValidationError()
else:
form = EditUserForm()
variables = RequestContext(request, {
'form': form, 'user': request.user
})
return render_to_response(
'registration/Edit_User.html',
variables
)
This is in my forms.pyclass
EditUserForm(forms.Form):
oldPassword = forms.CharField(
label=u'Current Password',
widget = forms.PasswordInput()
)
password1 = forms.CharField(
label=u'New Password',
widget=forms.PasswordInput()
)
password2 = forms.CharField(
label=u'New Password (Again)',
widget=forms.PasswordInput()
)
def clean_password2(self):
if 'password1' in self.cleaned_data:
password1 = self.cleaned_data['password1']
password2 = self.cleaned_data['password2']
if password1 == password2:
return password2
raise forms.ValidationError('Passwords do not match.')
The problem is in your clean_oldPassword method. You call check_password on the string 'oldPassword', instead of the variable, so naturally it fails.
You should also ensure that your template displays form.errors, so you can see why it doesn't validate.