forms组件钩子

局部钩子

对字段校验有特殊的需求,此时可以使用局部钩子来增加校验内容 下面的例子中判断前端页面传过来的user是否是以yuan开头的字符串

示例代码 点击is_valid,引入ValidationError

from django.core.exceptions import ValidationError
class UserForm(forms.Form):

    user = forms.CharField(
        label="用户名",  #自定义form表单显示到网页的名字
        min_length=5,
        error_messages={"required": "不能为空", "min_length": "最小长度不能小于5"},  #自定义错误提示信息
        widget=widgets.TextInput(attrs={"class": "form-control"})  #自定义属性,添加一个Bootstrap样式
    )

	def clean_user(self): #clean_是固定写法
		val = self.cleaned_data.get("user") #cleaned_data里面是经过form组件默认规则校验后的内容
		if val.startswith("yuan"): #如果校验通过则返回原值
			return val
		else: #如果校验未通过则抛错
			raise ValidationError("用户名必须以yuan开头")

解析源码 由于最终校验是使用is_valid方法,那么我们看看is_valid方法里面有什么

def is_valid(self):
	"""
	Returns True if the form has no errors. Otherwise, False. If errors are
	being ignored, returns False.
	"""
	return self.is_bound and not self.errors #只有self.errors为空是才返回True
@property
def errors(self):
	"Returns an ErrorDict for the data provided for the form"
	if self._errors is None: #第一次运行当然为None
		self.full_clean() #去看看full_clean执行了什么
	return self._errors
def full_clean(self):
	...
	self._clean_fields() #校验字段,这就是局部钩子关键所在,进去看看
	self._clean_form()

_clean_fields中将校验字段及规则做循环

def _clean_fields(self):
	for name, field in self.fields.items(): #self.fields就是需要校验的字段,{"user":user字段的规则对象}
        ...
		try:
            ...
			else:
                value = field.clean(value) #判断是否符合校验规则,不符合直接抛错,符合则继续往下走
			self.cleaned_data[name] = value #如果校验成功则存入cleaned_data字典
			if hasattr(self, 'clean_%s' % name): #钩子来源于这里,通过反射查询当前类下有没有一个叫'clean_%s' % name的方法
				value = getattr(self, 'clean_%s' % name)() #执行这个方法,返回值交给value
				self.cleaned_data[name] = value
		except ValidationError as e:
			self.add_error(name, e) #如果报错,则将字段名和错误信息组成键值对放入到errors字典中去

全局钩子

校验的规则涉及到多个字段值 比如:判断两次密码是否一致 全局错误在errors中的键值为"all" 通过form.errors["all"][0]来获取

示例代码 举例判断用户名和电话是否一致

def clean(self): #函数名是固定写法
	user = self.cleaned_data.get("user")
	tel = self.cleaned_data.get("tel")
	if user == tel:
		return self.cleaned_data #如果验证成功的固定写法
	else:
		raise ValidationError("用户名与电话不同")

源码解析

def full_clean(self):
	...
	self._clean_fields() 
	self._clean_form() #全局钩子
def _clean_form(self):
	try:
		cleaned_data = self.clean() #执行clean方法
	except ValidationError as e: #如果报错同样可以捕获到
		self.add_error(None, e) #做全局校验没有具体的字段,这个键值叫做'__all__'
	else:
		if cleaned_data is not None:
			self.cleaned_data = cleaned_data
def clean(self):
	"""
	Hook for doing any extra form-wide cleaning after Field.clean() has been
	called on every field. Any ValidationError raised by this method will
	not be associated with a particular field; it will have a special-case
	association with the field named '__all__'.
	"""
	return self.cleaned_data #自己定义的clean也要返回self.cleaned_data

这个clean就是留给我们的接口,我们自己写一个clean就不需要走这里了 全局错误都在form.errors["all"][0]中