首先,我们看看能正常验证两次密码一致性的注册表单 :
from __future__ import unicode_literals
import unicodedata
from django import forms
from django.contrib.auth import (
authenticate, get_user_model, password_validation,
)
from django.forms.widgets import TextInput
class UsernameField(forms.CharField):
def to_python(self, value):
return unicodedata.normalize('NFKC', super(UsernameField, self).to_python(value))
class UserRegisterForm(forms.ModelForm):
"""
学生注册表单
"""
password1 = forms.CharField(
label="确认密码",
widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': '请输入确认密码'}),
strip=False,
help_text="请输入确认密码")
password2 = forms.CharField(
label="确认密码",
widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': '请输入确认密码'}),
strip=False,
help_text="请输入确认密码",
)
class Meta:
model = User
fields = ("username",)
field_classes = {'username': UsernameField}
widgets = {
'username': TextInput(attrs={'class': 'form-control', 'placeholder': '请输入您的姓名'}),
}
def clean_password2(self):
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
print("p2---\n",type(password2), password2)
if password1 and password2 and password1 != password2:
raise forms.ValidationError("两次密码不一致")
# self.instance.username = self.cleaned_data.get('username')
# password_validation.validate_password(self.cleaned_data.get('password2'), self.instance)
return password2
本段代码链接:https://gitee.com/yinzhuoqun/StudentsManager/blob/master/students/views.py
假设现在把上面的 UserRegisterForm 表单的 clean_password2 改成 clean_password1 后,form.is_valid() 验证的时候会出现什么现象呢?
答案是:会出现两次密码的一致性验证失效,这到底是为啥?在代码里的表现就是,在 clean_password1 里面 get password2 的值是空的,跳过了密码一致性验证的逻辑。
System check identified no issues (0 silenced).
March 06, 2020 - 18:47:27
Django version 2.2.7, using settings 'StudentsManager.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
p2---
{'username': '张4全', 'password1': 'django123456'}
p2 <class 'NoneType'> None
[06/Mar/2020 18:47:36] "POST /register HTTP/1.1" 200 2192
查询 django 文档 有这么几句话:
Field子类的clean() 方法。它负责以正确的顺序运行to_python、validate 和 run_validators 并传播它们的错误。如果任何时刻、任何方法引发ValidationError,验证将停止并引发这个错误。这个方法返回验证后的数据,这个数据在后面将插入到表单的 cleaned_data 字典中。
表单子类中的clean_<fieldname>() 方法 —— <fieldname> 通过表单中的字段名称替换。这个方法完成于特定属性相关的验证,这个验证与字段的类型无关。这个方法没有任何传入的参数。你需要查找self.cleaned_data 中该字段的值,记住此时它已经是一个Python 对象而不是表单中提交的原始字符串(它位于cleaned_data 中是因为字段的clean() 方法已经验证过一次数据)。
例如,如果你想验证名为serialnumber 的CharField 的内容是否唯一, clean_serialnumber() 将是实现这个功能的理想之处。你需要的不是一个特别的字段(它只是一个CharField),而是一个特定于表单字段特定验证,并规整化数据。
这个方法返回从cleaned_data 中获取的值,无论它是否修改过。
表单子类的clean() 方法。这个方法可以实现需要同时访问表单多个字段的验证。这里你可以验证如果提供字段A,那么字段B 必须包含一个合法的邮件地址以及类似的功能。 这个方法可以返回一个完全不同的字典,该字典将用作cleaned_data。
我的理解是,还没被 clean() 验证的字段不会放入到 cleaned_data 中, 当要验证码 该字段时才放入到 cleaned_data 中。这样就会导致没验证 password2 时,在验证 clean_password1 中 password2 的值为 None.
Django 表单验证的源码剖析: