Почему метод clean_password2() работает, но не clean_password1() в Django usercreationform
Я пытаюсь понять, почему это работает, если кто-нибудь может мне это объяснить.
Я только что создал пользовательскую модель пользователя (показана ниже) и для проверки пароля она использует метод clean_password2(self): (показан ниже), однако когда я пытаюсь использовать clean_password1(self): (показан ниже), проверка не работает. почему? Уверен, что использование password1 или password2 для очистки данных будет работать, поскольку они одинаковые?
В документации Django говорится, что мы можем использовать методы clean_<fieldname>(): для очистки/проверки данных, и поскольку password1 является именем поля, я думаю, это должно работать.
Пользовательская модель пользователя
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
Это работает
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
Это не
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
Резюме: не ссылайтесь на другие поля в методах clean_<fieldname>. Вместо этого делайте эту логику в методе clean. https://docs.djangoproject.com/en/stable/ref/forms/validation/#cleaning-and-validating-fields-that-depend-on-each-other
Почему глубоко проникает в формы Django. Метод, вызывающий clean_<fieldname>, следующий:
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)
В _clean_fields каждое поле разрешается в том порядке, в котором оно появляется на форме, и в первый раз для этого поля заполняется cleaned_data. Поскольку password1 находится перед password2 в списке полей, когда выполняется clean_password1, cleaned_data["password2"] еще не был установлен. Вот что происходит с вашим кодом (я добавил комментарии):
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
Причина, по которой clean_password2 работает, заключается в том, что password2 идет позже в списке полей.