Django modelform clean_password2 - django

Stumbled this def clean_password2 ModelForm.
My question is does every time this we run this view. Does it will automatically run clean_password2 to check the password or do we need to explicitly call it?
Form.py
class RegisterForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
class Meta:
model = User
fields = ('full_name', 'email',) #'full_name',)
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 forms.ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super(RegisterForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
user.is_active = False # send confirmation email via signals
# obj = EmailActivation.objects.create(user=user)
# obj.send_activation_email()
if commit:
user.save()
return user
https://docs.djangoproject.com/en/4.0/topics/auth/customizing/#a-full-example

No, you don't have to call it explicitly as it says in the doc.
The clean_<fieldname>() method is called on a form subclass – 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. This method is not passed any parameters. You will need to look up the value of the field in self.cleaned_data and remember that it will be a Python object at this point, not the original string submitted in the form (it will be in cleaned_data because the general field clean() method, above, has already cleaned the data once).
doc: https://docs.djangoproject.com/en/4.0/ref/forms/validation/

Related

django custom Registration Form which inherits from "UserCreationForm"

I have a small question about Django so I have a custom Registration Form which inherits from "UserCreationForm" and therefore I use "fields" to define the fields I want to display. For this test just ("username", "email",). But when I go to see my form which is displayed "Password" and "Password confirmation" are also present. Wanting to understand, I therefore went to see the UserCreationForm class of django but this one has a Meta class which has fields = ("username"), so I don't really see why I have the passwords which are also present. I'm sorry the explanation is a bit laborious but I would like to understand so if anyone has a little explanation thank you very much.
My custom class:
class CustomSignupForm(UserCreationForm):
class Meta:
model = CustomUser
fields = ("username", "email",)
The UserCreationForm of django:
class UserCreationForm(forms.ModelForm):
"""
A form that creates a user, with no privileges, from the given username and
password.
"""
error_messages = {
"password_mismatch": _("The two password fields didn’t match."),
}
password1 = forms.CharField(
label=_("Password"),
strip=False,
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
help_text=password_validation.password_validators_help_text_html(),
)
password2 = forms.CharField(
label=_("Password confirmation"),
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
strip=False,
help_text=_("Enter the same password as before, for verification."),
)
class Meta:
model = User
fields = ("username",)
field_classes = {"username": UsernameField}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self._meta.model.USERNAME_FIELD in self.fields:
self.fields[self._meta.model.USERNAME_FIELD].widget.attrs[
"autofocus"
] = True
def clean_password2(self):
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise ValidationError(
self.error_messages["password_mismatch"],
code="password_mismatch",
)
return password2
def _post_clean(self):
super()._post_clean()
# Validate the password after self.instance is updated with form data
# by super().
password = self.cleaned_data.get("password2")
if password:
try:
password_validation.validate_password(password, self.instance)
except ValidationError as error:
self.add_error("password2", error)
def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
Here is what it gives:
Display of my form
The reason this happens is that the password1 and password2 field are defined in the UserCreationForm class. The form fields do not need to be listed in the fields attribute: all field objects in the class are form fields.
A ModelForm will in essence add form field objects to the class, and thus automates that process, but any field object defined in the class is a form field, that is how a simple Form [Django-doc] behaves, and a ModelForm is just a subclass of Form.
Based on your comment you want to reorder the fields. You can do so with the .field_order attribute [Django-doc]:
class CustomSignupForm(UserCreationForm):
field_order = ('username', 'email', 'password1', 'password2', 'newsletter')
class Meta:
model = CustomUser
fields = ('username', 'email', 'newsletter')

Create User in Django through form

I want to have a user sign up form in Django, I know that for the Backend I should have something like this:
>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user('john', 'lennon#thebeatles.com', 'johnpassword')
>>> user.last_name = 'Lennon'
>>> user.save()
However, I don't know how to make the Frontend. I've already looked up in the Django documentation and found the UserCreationForm class and it says it's deprecated.
What should I do? Thank you
Try something like this:
#forms.py
class UserCreationForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
class Meta:
model = MyUser
fields = ('email', 'date_of_birth')
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
You should read this section of the Django Docs on authentication.

How to add Choice Field to Django forms for user registration?

I am making a registration form for 3 types of users. When a user enters email and password he/she must select one of the roles.
First I used BooleanFields and it works, but more than one checkbox can be selected. I need that user can select only one role.
I have tried ChoiceField, which I could display on the template but it does not POST any data to db.
forms.py
class RegisterForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
parent = forms.BooleanField(label="I am a Parent")
school = forms.BooleanField(label="I am a School Admin")
vendor = forms.BooleanField(label="I am a Vendor")
role_select=forms.ChoiceField(
widget=forms.RadioSelect,
label="Select your role.",
choices=(('is_parent','parent '),('is_school','school'),('is_vendor','vendor')),
)
if not parent and not school and not vendor:
raise forms.ValidationError("Users must have a role")
class Meta:
model = User
fields = ['role_select', 'parent', 'school', 'vendor', 'email'] #'full_name',)
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 forms.ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super(RegisterForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
user.role_select( self.cleaned_data['role_select'])
# user.active = False # send confirmation email
if commit:
user.save()
return user
As you see in the forms.py I have a combination of two approaches. So it has some useless lines. Which approach to use and how?
you can use Django groups where you can create a group and assign it as a choicefield when user registers. You can check my repository. I have done the same thing here. Hope it can get you some idea
https://github.com/tsephel/User-authentication-django-/tree/master/env

Update custom user password using UpdateWithInlinesView from django-extra-views app

I have a model 'Admin' related to a (custom) user within a one to one field. To update all the fields I'm using django-extra-views app that basically use Django formset. It works well but for updating the password.
Models
class Users(AbstractUser):
# common fields for all the users
class Admins(models.Model):
# Custom fields for the admins
Forms:
class AdminProfileForm(forms.ModelForm):
class Meta:
model = Users
fields = ['first_name', 'last_name']
password1 = forms.CharField(widget=forms.PasswordInput, label='New Password')
password2 = forms.CharField(widget=forms.PasswordInput, label='Confirm Password')
Views:
class AdminInline(InlineFormSet):
model = Admins
fields = ['admins_fiels_here']
class AdminProfile(BaseContext, UpdateWithInlinesView):
....
inlines = [AdminInline]
form_class = AdminProfileForm
def get_object(self):
return Users.objects.get(pk=self.request.user.id)
def forms_valid(self, formset, inlines):
password1 = formset.cleaned_data['password1']
password2 = formset.cleaned_data['password2']
if password2 and password1:
if password1 != password2:
# raise a ValidationError message
user = self.request.user # at this point password is 'old_passord'
user.set_password(password1) # now is 'password1'
user.save() # 'password1' is correctly saved in db
return super(AdminProfile, self).forms_valid(formset, inlines)
As I describe above, the new password is saved but when super(AdminProfile, self).forms_valid(formset, inlines) ends its works the password results to be the old one.

Forms in Django Rest Framework

I'm able to create ModelSeralizers easily, but I am having a little trouble with my Forms. Is there a way to serialize the form below, or do I need to do each form in the native language if I take this to a mobile device?
class SetPasswordForm(forms.Form):
password1 = forms.CharField(label='New password',
widget=forms.PasswordInput(
attrs={'placeholder': 'New password'}))
password2 = forms.CharField(label='Verify new password',
widget=forms.PasswordInput(
attrs={'placeholder': 'Password again'}))
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
super(SetPasswordForm, self).__init__(*args, **kwargs)
def clean_password2(self):
password_length = settings.MIN_PASSWORD_LENGTH
password1 = self.cleaned_data.get("password1")
if len(password1) < password_length:
raise forms.ValidationError(
"Password must be longer than "
"{} characters".format(password_length))
password2 = self.cleaned_data.get("password2")
if password1 and password2:
if password1 != password2:
raise forms.ValidationError("Passwords do not match")
return password2
You can create a serializer named PasswordSerializer which performs the same checking and validation as you are doing above in SetPasswordForm.
We create a serializer having 2 fields password1 and password2.
password1 field has min_length argument passed to it which validates that the input contains no fewer than this number of characters. Also, we define the custom error message for the case in which input is less than min_length in an error_messages dictionary. Doing this removes the validations you were performing earlier in your form and now DRF will handle that for you. Also, the serializer fields have allow_null set to False. So, if None value is sent, DRF automatically handles that.
We need to create a validate() function which checks if the passwords match or not. If the 2 passwords do not match, serializer will raise a ValidationError.
The above code transformed to a DRF serializer will be something like:
from rest_framework import serializers
class PasswordSerializer(serializers.Serializer):
password1 = serializers.CharField(min_length=settings.MIN_PASSWORD_LENGTH, error_messages={'min_length': "Password must be longer than {} characters".format(settings.MIN_PASSWORD_LENGTH)})
password2 = serializers.CharField()
def validate(self, data):
if data['password1'] != data['password2']: # Check if the 2 passwords match
raise serializers.ValidationError("Passwords do not match")
return data