Extending form.is_valid() - django

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.

Related

Django Form not saving for Custom User Model

I have a register user form which is doing all the validation as expected. However, it is not saving. I am not able to figure out the reason. How do I debug it ? Any help ? I am a newbie to forms and formviews any good document with example would really help me.
class RegisterForm(forms.ModelForm):
phone_number = forms.IntegerField(required=True)
password1 = forms.CharField(widget=forms.PasswordInput())
password2 = forms.CharField(widget=forms.PasswordInput())
country_code = forms.IntegerField()
#schools = school.objects.all()
#school_name = forms.ModelChoiceField(queryset=school.objects.distinct())
MIN_LENGTH = 4
class Meta:
model = User
fields = ['username','country_code','phone_number', 'password1', 'password2',
'full_name' ]
def clean_phone_number(self):
phone_number = self.data.get('phone_number')
print(phone_number)
if User.objects.filter(phone_number=phone_number).exists():
raise forms.ValidationError(
_("Another user with this phone number already exists"))
if len(phone_number) == 10 and phone_number.isdigit():
pass
else:
raise forms.ValidationError(
_("Invalid Phone Number"))
return phone_number
def save(self, *args, **kwargs):
print("saving")
user = super(RegisterForm, self).save(*args, **kwargs)
user.set_password(self.cleaned_data['password1'])
print('Saving user with country_code', user.country_code)
user.save()
return user
Views.py
class RegisterView(SuccessMessageMixin, FormView):
template_name = 'register-2.html'
form_class = RegisterForm
success_message = "One-Time password sent to your registered mobile number.\
The verification code is valid for 10 minutes."
def form_valid(self, form):
full_name=self.request.POST["full_name"]
user = form.save()
print(user.id)
username = self.request.POST['username']
password = self.request.POST['password1']
user = authenticate(username=username, password=password)
kwargs = {'user': user}
self.request.method = 'POST'
print("User created")
The print in clean_phone_number works however, save does not work
I had issue in the my form. One of the field was disabled and the value was not captured because of that.
However to identify that I used
def form_invalid(self,form):
# Add action to invalid form phase
messages.error(self.request, form.errors)
return self.render_to_response(self.get_context_data(form=form))

clean(self) method is not called when if form.is_valid()

I have a RegisterUserForm that is bound to a CustomUser (that extends AbstractUser). Before I save the user, I want to check password1 and password2 if they are equal but the clean(self) method is not called. the user is created even if the passwords are not the same.
I have read a lot of questions and tried a lot of things but couldn't find an answer. My code is below
I tried adding an init and a save method but still does not work.
class RegisterUserForm(forms.ModelForm):
password1=forms.CharField(label=_("password"), min_length=4)
password2=forms.CharField(label=_("repeat password"), min_length=4)
class Meta:
model = CustomUser
fields = ('first_name', 'last_name', 'email', 'password1','password2')
def save(self, *args, **kwargs):
self.full_clean()
return super().save(*args, **kwargs)
def clean(self):
cleaned_data = super().clean()
print("clean is called")
password1 = form.cleaned_data.get("password1")
password2 = form.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise ValidationError('password_mismatch')
return self.cleaned_data
and my view is
def register(request):
if request.method=='POST':
form = RegisterUserForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.set_password(form.cleaned_data['password1'])
user.save()
email = user.email
messages.success(request, f'user with {email} created successfully')
return redirect('home')
else:
form = RegisterUserForm()
return render(request, 'users/register.html', {'form':form })

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

Django Register Form 'AnonymousUser' object has no attribute 'backend'

I have the following register view that enters a new user.
I want it to enter the new user and then log in automatically.
It saves through the User record but returns this error when trying to login:
'AnonymousUser' object has no attribute 'backend'
views.py
def register(request):
if request.method == 'POST':
form = UserRegisterForm(request.POST, error_class=DivErrorList)
if form.is_valid():
form.save()
new_user = authenticate(username=request.POST['username'],password=request.POST['password'])
login(request, new_user)
return HttpResponseRedirect('/production/')
else:
form = UserRegisterForm(error_class=DivErrorList)
return render(request,'register.html', {
'form': form,
})
forms.py
class UserRegisterForm(forms.ModelForm):
class Meta:
model = User
fields = ('username','first_name','last_name','email','password')
password_compare = forms.CharField(max_length=128)
def __init__(self, *args, **kwargs):
super(UserRegisterForm, self).__init__(*args, **kwargs)
self.fields['password_compare'].label = 'Password Again'
self.fields['password'].help_text = ''
self.fields['first_name'].label = 'First Name'
self.fields['last_name'].label = 'Last Name'
self.fields['email'].label = 'E-mail Address'
def clean(self):
cleaned_data = self.cleaned_data
password1 = cleaned_data.get('password', None)
password2 = cleaned_data.get('password_compare', None)
if not (password1):
error_msg = u'This field is required.'
self._errors['password'] = self.error_class([error_msg])
if not (password2):
error_msg = u'This field is required.'
self._errors['password_compare'] = self.error_class([error_msg])
# password fields must match
if password1 != password2:
error_msg = u'Password doesn\'t match the confirmation.'
self._errors['password'] = self.error_class([error_msg])
del cleaned_data['password']
# cannot have a username already existing
try:
existing_user = User.objects.get(username=cleaned_data.get('username'))
error_msg = u'Username already exists.'
self._errors['username'] = self.error_class([error_msg])
del cleaned_data['username']
return cleaned_data
except User.DoesNotExist:
return cleaned_data
Your user will never authenticate, because you're saving the password in plain text - and authenticate expects a hashed password. You should call user.set_password(password) on the newly-created user object before saving it to the db - see the built-in UserCreationForm.
I had the same error for a newly registering user and it left me frustrated for an hour.
There was a piece of code that tried to log user in right after the registration.
Usually it worked just fine, but not this time.
def attempt_login(self, email, password):
user = authenticate(username=email, password=password)
login(self.request, user)
return user
It seemed that authenticate returned None, and then calling login with None caused this exception. But I was sure the User has been created after registration.
Finally, I realized that this particular user's login was longer than 30 characters, and the form field had no validation. The login would get truncated in the database, and therefore authenticate was called for non-existent login.

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

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"