为什么 clean_password2() 方法有效但 Django usercreationform 中的 clean_password1() 无效

why does clean_password2() method work but not clean_password1() in Django usercreationform

如果有人可以向我解释,我正在尝试弄清楚为什么这会起作用。

我刚刚创建了一个自定义用户模型(如下所示),它使用 clean_password2(self):(如下所示)方法进行密码验证,但是当我尝试使用 clean_password1(self):(如下所示)时) 验证不起作用。为什么?当然使用 password1password2 来清理数据是可行的,因为它们是相同的?

Django 文档声明我们可以对 clean/validate 数据使用 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。因为字段列表中password1password2之前,当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 出现在字段列表的后面。