Почему метод 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 идет позже в списке полей.

Вернуться на верх