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
Related
I am trying to figure out why this works if someone could maybe explain it to me.
I've just created a custom user model (shown below) and for the password validation it uses the clean_password2(self): (shown below) method however when I try to use clean_password1(self): (shown below) the validation does not work. why? Surely using either password1 or password2 to clean the data would work since they are the same?
Django docs state that we can use clean_<fieldname>(): methods to clean/validate data and since password1 is a fieldname in my mind that should work.
Custom user model
class UserManager(BaseUserManager):
def create_user(self, email, password=None):
if not email:
raise ValueError("Users must have an email address")
user = self.model(email=self.normalize_email(email))
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password=None):
user = self.create_user(email=email, password=password)
user.is_staff = True
user.is_admin = True
user.save(using=self._db)
return user
class User(AbstractBaseUser):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
email = models.EmailField(verbose_name="Email Address", max_length=255, unique=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = "email"
REQUIRED_FIELDS = []
def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
return True
def has_module_perms(self, app_label):
return True
This works
class UserCreationForm(forms.ModelForm):
password1 = forms.CharField(
label="Password",
help_text=password_validation.password_validators_help_text_html(),
widget=forms.PasswordInput,
)
password2 = forms.CharField(
label="Confirm Password",
help_text="Enter the same password as before for validation",
widget=forms.PasswordInput,
)
class Meta:
model = User
fields = ["email"]
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
This doesn't
def clean_password1(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise ValidationError("Passwords don't match")
return password1
Summary: don't reference other fields in clean_<fieldname> methods. Do this logic in the clean method instead. https://docs.djangoproject.com/en/stable/ref/forms/validation/#cleaning-and-validating-fields-that-depend-on-each-other
The why goes deep into Django forms. The method that calls clean_<fieldname> is the following:
def _clean_fields(self):
for name, field in self.fields.items():
# value_from_datadict() gets the data from the data dictionaries.
# Each widget type knows how to retrieve its own data, because some
# widgets split data over several HTML fields.
if field.disabled:
value = self.get_initial_for_field(field, name)
else:
value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
try:
if isinstance(field, FileField):
initial = self.get_initial_for_field(field, name)
value = field.clean(value, initial)
else:
value = field.clean(value)
self.cleaned_data[name] = value
if hasattr(self, 'clean_%s' % name):
value = getattr(self, 'clean_%s' % name)()
self.cleaned_data[name] = value
except ValidationError as e:
self.add_error(name, e)
In _clean_fields, each field is resolved in the order they appear on the form, and this is the first time cleaned_data is filled in for that field. Because password1 comes before password2 in the fields list, when clean_password1 is run, cleaned_data["password2"] has not yet been set. Here is what happens to your code (I added comments):
def clean_password1(self):
password1 = self.cleaned_data.get("password1")
# `"password2"` is not present in cleaned_data, so `password2` is set to None
password2 = self.cleaned_data.get("password2")
# With `password2 == None`, this condition resolves to false
if password1 and password2 and password1 != password2:
raise ValidationError("Passwords don't match")
# `password1` is returned without any validation error
return password1
The reason clean_password2 works is because password2 comes later in the field list.
When i finished enter my username and password, this error is blow up, he write that username and password didn't match, but all data is valide and true! how to fix it?
How to check the password, if the password in the database is stored in encrypted form, and the supplied password in the form of a string!
Thank you all for your help, I will look forward to your advice!
forms.py
class UserLogInForm(forms.Form):
username = forms.RegexField(regex=r'^\w+$', widget=forms.TextInput(attrs=dict(required=True, max_length=30)), label=_("Username"), error_messages={ 'invalid': _("This value must contain only letters, numbers and underscores.") })
password = forms.CharField(widget=forms.PasswordInput(attrs=dict(required=True, max_length=30, render_value=False)), label=_("Password"))
def clean_username(self):
user = User.objects.get(username__iexact=self.cleaned_data['username'])
if user:
return self.cleaned_data['username']
else:
raise forms.ValidationError('This user does not exist!')
def clean(self):
username = self.cleaned_data['username']
password = self.cleaned_data['password']
user = User.objects.filter(username=username)
if user.count() == 1:
user = user.first()
if user.check_password(password):
raise forms.ValidationError("Incorrect password!")
return self.cleaned_data
else:
raise forms.ValidationError('This user does not exist!')
views.py
def login_view(request):
form = UserLogInForm(request.POST or None)
if form.is_valid():
username = form.cleaned_data['username'],
password = form.cleaned_data['password'],
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
return redirect('/')
else:
return redirect('accounts/login')
context = {'form':form}
return render(request, 'accounts/registration/login.html', context)
You are raising the error when the check_password() returns True.
I suggest you may re-write the method something like this,
def clean(self, *args, **kwargs):
username = self.cleaned_data.get("username")
password = self.cleaned_data.get("password")
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
raise forms.ValidationError("This user does not exist!")
if user and not user.check_password(password):
raise forms.ValidationError("Incorrect password!"):
if user and not user.is_active:
raise forms.ValidationError("This user is no longer active.")
return super(UserLogInForm, self).clean(*args, **kwargs)
Also, remove the trailing commas from these lines in your view,
username = form.cleaned_data['username']
password = form.cleaned_data['password']
Due to the trailing commas, python returns a tuple rather than a string.
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
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.
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"