Django之forms组件及forms组件校验源码

1、forms组件

1.1、定义

1、注册功能,登录功能,前端需要校验(字段长度,邮箱是否合法…)
2、前端校验可以没有,后端校验是必须的,使用传统方式if判断写的很多
3、借助于forms组件,可以快速实现字段的校验,需要导入:from django.forms import Form

1.2、forms校验字段功能
1.2.1、定义类,类里写要校验的字段
class MyForm(forms.Form):
    # 校验这个字段,最大长度是32,最小长度是3
    name = forms.CharField(required=False, max_length=32, min_length=3,label='用户名')
    email = forms.EmailField(label='邮箱')
    age=forms.IntegerField(max_value=130,min_value=0,label='年龄')
1.2.2、视图函数
# 需要导入刚才定义的类
from myforms import MyForm

def check_user_info(request):
    # 数据可以是从前端传过来的,也可以是自己后台的数据
    # 校验数据是否合法
    # 实例化得到form对象,把要校验的数据传入
    # 校验数据只校验类中出现的字段 多传不影响 多传的字段直接忽略
    # 校验数据 默认情况下 类里面所有的字段都必须传值
    # 也就意味着校验数据的时候 默认情况下数据可以多传但是绝不可能少传
    form=myforms.MyForm(request.POST)
    
    # 校验数据,判断数据是否合法: form.is_valid() 返回布尔类型,该方法只有在所有的数据全部合法的情况下才会返回True
    if form.is_valid():
    	# 成功
        print('校验通过')
        # 查看所有校验通过的数据
        print(form.cleaned_data)  # 不一定是上面传入的数据
    else:
    	# 校验失败
        print(form.cleaned_data)
        # 查看所有不符合校验规则以及不符合的原因
        print(form.errors)  # 默认是form.errors.as_ul()  是为了渲染模板
        print(type(form.errors))  # from django.forms.utils import ErrorDict
        
        # 重写了__str__
        print(form.errors.as_json())
        print(form.errors.as_data())
    return HttpResponse('ok')
1.3、forms渲染模板功能
1.3.1、视图函数
def check_user_info(request):
    if request.method == 'GET':
        form=myforms.MyForm()
        return render(request,'check_user_info.html',{'form': form})
    elif request.method == 'POST':
        # 数据校验
        form=myforms.MyForm(request.POST)
        if form.is_valid():
            print('校验通过,存数据库')
        else:
            print(form.errors.as_data())
            print('校验失败,返回错误')
        return HttpResponse('ok')
1.3.2、模板
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<hr>
<h1>手动创建模板</h1>
<form action="" method="post">
    <p>用户名: <input type="text" name="name"></p>
    <p>邮箱: <input type="text" name="email"></p>
    <p>年龄: <input type="text" name="age"></p>
    <p><input type="submit" value="提交"></p>
</form>

<h1>半自动渲染模板1</h1>
<form action="" method="post">
    <p>用户名: {{ form.name }}</p>
    <p>邮箱: {{ form.email }}</p>
    <p>年龄: {{ form.age }}</p>
    <p><input type="submit" value="提交"></p>
</form>

<h1>半自动渲染模板2(使用频率高)</h1>
<form action="" method="post">
    <p>{{ form.name.label }}: {{ form.name }}</p>
    <p>{{ form.email.label }}: {{ form.email }}</p>
    <p>{{ form.age.label }}: {{ form.age }}</p>
    <p><input type="submit" value="提交"></p>
</form>

<h1>半自动渲染模板3(使用频率高)</h1>
<form action="" method="post">
    {% for foo in form %}
       <p>{{ foo.label }}: {{ foo }}</p>
    {% endfor %}
    <p><input type="submit" value="提交"></p>
</form>

<h1>全自动</h1>
<form action="" method="post">
	<!--使用ul渲染-->
	{{ form.as_ul }}
	<!--使用p标签渲染-->
	{{ form.as_p }}
	<!--使用table渲染-->
	<table>
		{{ form.as_table }}
	</table>
    <p><input type="submit" value="提交"></p>
</form>
</body>
</html>
1.4、forms渲染错误信息
1.4.1、视图函数
def register(request):
    if request.method == 'GET':
        form=myforms.MyForm()
        return render(request, 'register.html',{'form':form})
    else:
        form=myforms.MyForm(request.POST)
        if form.is_valid():
        	# 校验成功模拟跳转百度页面
            return redirect('http://www.baidu.com')
        else:
            return render(request, 'register.html',{'form':form})
1.4.2、模板
<form action="" method="post" novalidate>
	{% for foo in form %}
	<div class="form-group">
        <label for="">{{ foo.label }}</label>
            {{ foo }}
            <span class="text-danger pull-right">{{ foo.errors }}</span>
        </div>
	{% endfor %}
	<div class="text-center">
		<input type="submit" value="提交" class="btn btn-danger">
	</div>
</form>
1.5、forms组件参数配置
定制模板中的显示样式,及配置类,比如widget=widgets.PasswordInput(attrs={'class': 'form-control'})
错误信息中文显示,比如error_messages={'min_length': '字段长度过短'}

from django.core.exceptions import ValidationError
from django.forms import widgets

class MyForm(forms.Form):
	# 用户名至少3位,最多16位
    username = forms.CharField(min_length=3, max_length=16, label='用户名',
                               widget=widgets.TextInput(attrs={'class': 'form-control'}),
                               error_messages={'required': '用户名必传', 'min_length': '用户名至少3位', 'max_lenth': '用户名最多16位'})
    age = forms.IntegerField(min_value=1, max_value=130, label='年龄',
                             widget=widgets.TextInput(attrs={'class': 'form-control'}), error_messages={
            'required': '年龄必传', 'min_value': '最少1岁', 'max_value': '最大130岁'})
    password = forms.CharField(min_length=3, max_length=16, label='密码',
                               widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    re_password = forms.CharField(min_length=3, max_length=16, label='确认密码',
                                  widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(label='邮箱', widget=widgets.TextInput(attrs={'class': 'form-control'}))
    text = forms.CharField(label='个人简介', widget=widgets.Textarea(attrs={'class': 'form-control'}))
    date = forms.CharField(label='出生日期', widget=widgets.DateInput(attrs={'class': 'form-control'}))
1.6、钩子函数(HOOK)
在特定的节点自动触发完成响应操作
钩子函数在forms组件中就类似于第二道关卡,能够让我们自定义校验规则,钩子函数在类里面书写方法即可
在forms组件中有两类钩子
	1、局部钩子
		当你需要给单个字段增加校验规则的时候可以使用
	2、全局钩子
  		当你需要给多个字段增加校验规则的时候可以使用
1.6.1、局部钩子
校验用户名中不能含有“xxx”

局部钩子的使用
1、在自定义的Form类中写 clean_字段名
2、取出字段的真正值,name=self.cleaned_data.get('username')
3、判断自己的规则,如果判断失败,抛出ValidationError或使用self.add_error
4、如果通过,return name

def clean_username(self):
    username = self.cleaned_data.get('username')
    if username.startswith('xxx'):
    	# 方式一 抛出异常ValidationError
        # raise ValidationError('不能以xxx开头')
		
		# 方式二 为某个字段添加错误信息
        self.add_error('username', '不能以xxx开头')
    else:
    	# 校验通过,返回username
        return username
1.6.2、全局钩子
校验两次密码填写是否一致

def clean(self):
    password = self.cleaned_data.get('password')
    re_password = self.cleaned_data.get('re_password')
    if password != re_password:
    	# 方式一 抛出异常ValidationError
    	# raise ValidationError('两次密码不一致')

		# 方式二 为某个字段添加错误信息
        self.add_error('re_password', '两次密码不一致')
    # 校验通过,返回
    return self.cleaned_data

2、forms组件校验源码

1、form_obj.is_valid()
def is_valid(self):
        """
        Returns True if the form has no errors. Otherwise, False. If errors are
        being ignored, returns False.
        """
   # 如果is_valid要返回True的话 那么self.is_bound要为True self.errors要为Flase
   return self.is_bound and not self.errors

2、self.errors: 这个self指的是BaseForm类,errors使用了property修饰,其实是一个方法
3、self.full_clean(): 这个self指的是BaseForm类
4、forms组件所有的功能基本都出自于full_clean方法
def full_clean(self):
  	self._clean_fields()  # 校验字段 + 局部钩子
    self._clean_form()  # 全局钩子
    self._post_clean()

# 校验字段 + 局部钩子
self._clean_fields: 这个self指的是BaseForm类
		for name, field in self.fields.items():
            try:
                # 字段自己的校验(比如最大值,最小值,是不是邮箱格式等)
                value = field.clean(value)
                self.cleaned_data[name] = value
                if hasattr(self, 'clean_%s' % name): # 反射判断有没有clean_字段名,局部钩子
                    value = getattr(self, 'clean_%s' % name)()
                    self.cleaned_data[name] = value
            except ValidationError as e:
                self.add_error(name, e)
                
# 全局钩子              
self._clean_form: 这个self指的是BaseForm类
	 try:
        cleaned_data = self.clean()  # self.clean执行的是自己类的clean方法
     except ValidationError as e:
        self.add_error(None, e)

总结
is_valid() -->self.errors–>self.full_clean()
第一步:self._clean_fields()方法中先会校验form中定义的第一个字段,比如name,然后校验max_lenth,min_length等,校验成功后会查看是否有局部钩子clean_data,如果有就会执行,执行完毕后执行第二步,如果没有直接执行第二步;

第二步:再去校验第二个字段,重复第一步,先校验内置的,再校验局部钩子;

第三步:所有字段都校验通过(字段和局部钩子都成功),校验全局钩子

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值