from组件之钩子函数、常用字段、modelform介绍、cookie与session
今日学习内容总结
from组件
from组件之钩子函数
钩子函数
1. 全局和局部钩子都是定义在forms组件内的函数
2. 钩子函数类似于校验的第二道关卡,能够让我们自定义校验规则
两种钩子函数
1. 局部钩子:用于给单个字段增加校验规则
2. 全局钩子: 用于给多个字段增加校验规则
两种钩子函数的书写方式
局部钩子
def clean_字段名(self):
字段值 = self.cleaned_data.get('字段名')
[判断逻辑代码]
[不通过抛出 ValidationError 异常]
[通过则返回该字段值]
全局钩子
def clean(self):
字段值1 = self.cleaned_data.get('字段1')
字段值2 = self.cleaned_data.get('字段2')
[判断逻辑代码]
[不通过抛出 ValidationError 异常]
[通过则 return self.cleaned_data]
forms组件常用字段
初始值 : initial
username = forms.CharField(
min_length=8,
label="用户名",
initial="shawn" # 设置默认值
)
自定义错误提示 : error_massages
username = forms.CharField(
min_length=8,
label="用户名",
initial="shawn",
error_messages={
"required": "不能为空",
"invalid": "格式错误",
"min_length": "用户名最短8位"
}
)
密码框:password
# 设置render_value在输入错误的时候, 保留输入的内容value值
pwd = forms.CharField(
min_length=4,
label="密码",
widget=forms.widgets.PasswordInput(attrs={'class': 'a1'}, render_value=True)
)
反选radio
gender = forms.fields.ChoiceField(
choices=((1, "男"), (2, "女"), (3, "未知")),
label="性别",
initial=3,
widget=forms.widgets.RadioSelect()
)
单选checkbox
keep = forms.ChoiceField(
label="是否记住密码",
initial="checked",
widget=forms.widgets.CheckboxInput()
)
多选checkbox
Copyhobbies1 = forms.MultipleChoiceField(
choices=((1, "read"), (2, "run"), (3, "play"),),
label="爱好",
initial=[1, 3],
widget=forms.widgets.CheckboxSelectMultiple()
)
单选Select
Copyeducation = forms.ChoiceField(
choices=((1, "高中"), (2, "中专"), (3, "大专"), (4, "本科"), (5, "研究生"), (6, "硕士")),
label="学历",
initial=3,
widget=forms.widgets.Select(attrs={'class': 'form-control'})
)
多选Select
Copyhobbies2 = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=[1, 2],
widget=forms.widgets.SelectMultiple(attrs={'class': 'form-control'})
)
forms组件源码分析
引入
1. 分析forms 组件如何进行校验
2. 为何将校验成功的数据放入 cleaned_data 中
3. 检验失败的数据放入 errors 中
4. 代码示例
# 进行校验 is_valid()
if form_data.is_valid():
print('校验成功')
# 获得校验成功的数据 cleaned_data
print(form_data.cleaned_data)
else:
print('检验失败')
# 获得检验失败的错误提示 errors
print(form_data.errors)
error = form_data.errors.get('__all__')
forms组件的检验机制
forms组件最核心的就是 is_valid( ) 这个校验机制, 也就是我们的突破口。
1. Ctrl + 点击 is_valid( ) 查看其内部源码
def is_valid(self):
"""Return True if the form has no errors, or False otherwise."""
# 如果表单没有错误则返回True,否则返回False
return self.is_bound and not self.errors
也就是说让表单验证成功就必须保证 self.is_bound 为 True , self.errors 为 False。
2. 点击 self.is_bound 查看其源码
self.is_bound = data is not None or files is not None
只要数据部位空或者文件不为空, self.is_bound 就为 True, 也就是只要你传值了就为 True。
3. 再返回上一步点击 self.errors 查看源码
@property # 类装饰器,可以伪装成属性来使用(不需要加括号)
def errors(self):
"""Return an ErrorDict for the data provided for the form."""
# 为表单提供的数据返回ErrorDict。
if self._errors is None: # 如果_errors中为空则执行full_clean()
self.full_clean()
return self._errors
4.点击进入 self._error
self._errors = None # Stores the errors after clean() has been called.
发现其默认就是 None, 也就是说:无论如何都会执行 self.full_clean()。
5.在进入到 full_clean() 中查看
def full_clean(self):
"""
Clean all of self.data and populate self._errors and self.cleaned_data.
"""
# 对所有的self.data、self._errors、self.cleaned_data进行校验
# 这里赋值了一个错误字典用来存放错误字段
self._errors = ErrorDict()
if not self.is_bound: # Stop further processing(停止处理其他规则)
return
# 这个字典用来存正确字段
self.cleaned_data = {}
......
self._clean_fields() # 字段校验和局部钩子方法
self._clean_form() # 全局钩子
self._post_clean() # 内部为pass,但可以自己添加
6.我们在来看看校验字段的方法 _clean_fields()
def _clean_fields(self):
# 循环从fields中取出name和field
# fields实质上是一个字典,forms组件的实例化就会自动创建一个fields
# 该字典类内部类似 : {'name':'name字段对象','pwd':'pwd字段对象'}
for name, field in self.fields.items():
......
try:
# 判断是不是文件,是文件则走下面的逻辑
if isinstance(field, FileField):
initial = self.get_initial_for_field(field, name)
value = field.clean(value, initial)
else:
# 不是文件则进行这一步,使用规则对象对传入的value进行校验(clean()才是真正的进行校验)
value = field.clean(value)
# 然后将值加到字典中去,不成功则最下面抛出异常,将字段和错误信息加入到错误字典中
self.cleaned_data[name] = value
# 判断是否有'clean_%s'格式的一个变量名,也就是我们写的局部钩子函数(解释了为什么钩子函数名要那样写)
if hasattr(self, 'clean_%s' % name):
# 有的话就拿出来执行该钩子函数,得到返回数据
value = getattr(self, 'clean_%s' % name)()
# 将返回的数据添加到字典中,如果出错则也是走下面的except
self.cleaned_data[name] = value
except ValidationError as e:
# 将错误字段名和错误信息加入到errors字典中
# 我们使用raise ValidationError('')抛出异常,与下面这个方法的效果类似
self.add_error(name, e)
7.点击 add_error 查看关键信息
def add_error(self, field, error):
......
......
if field in self.cleaned_data: # 如果field字段对象在cleaned_data
del self.cleaned_data[field] # 那么就将其从中删除
8.再来看看校验全局钩子的方法_clean_form()
def _clean_form(self):
try:
cleaned_data = self.clean() # 执行全局钩子并拿到返回值(父类中有clea()方法,如果你自己写了则执行你写的)
except ValidationError as e:
self.add_error(None, e) # 出错则添加错误信息
else:
if cleaned_data is not None: # 没出错则判断全局钩子的返回值是否为空
self.cleaned_data = cleaned_data # 不是空则原封不动的返回给elf.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__'.
"""
# 由这个方法引发的任何验证错误将与名为'__all__'的字段关联
# 父类中的全局钩子函数直接返回了cleaned_data
return self.cleaned_data
这就是为什么使用全局钩子的时候需要返回一个完整的cleaned_data, 否则将是一个不完整的数据。
ModelForm
ModelForm简介
forms组件主要配合models里面的默写类一起使用。但是默写类里面的字段需要在forms类中相当于重写一遍,代码冗余,为了更好的结合forms与models的关系,有了一个ModelForm(基于forms组件)。
代码案例:
class MyUser(forms.ModelForm):
class Meta:
model = models.User # 指定关联的表
fields = '__all__' # 所有的字段全部生成对应的forms字段
labels = {
'name': '用户名',
'age': '年龄',
'addr': '地址',
'email': '邮箱'
}
widgets = {
"name": forms.widgets.TextInput(attrs={"class": "form-control"}),
}
def reg(request):
form_obj = MyUser()
if request.method == 'POST':
form_obj = MyUser(request.POST)
if form_obj.is_valid():
# form_obj.save() # 新增数据
edit_obj = models.User.objects.filter(pk=5).first()
form_obj = MyUser(request.POST, instance=edit_obj) # 是新增还是保存就取决于有没有instance参数
form_obj.save() # 编辑数据
return render(request, 'reg.html', locals())
Cookie与Session
cookie简介
早期的互联网应用程序都是不保存用户状态的,所有人发送请求返回的都是相同的页面。
现如今几乎所有的应用程序都可以保存用户状态。如何实现的?
HTTP协议四大特性之一:无状态。让服务端知道你是谁的方式很单一:携带用户名和密码(身份标识)。每次操作之前都需要输入用户名和密码,当你成功登录之后浏览器会在本地帮你保存用户名和密码。每次操作浏览器自动发送用户名和密码。
cookie本质,指代服务端让客户端保存的数据(存储在客户端上与用户信息相关的数据)。
session简介
早期的cookie是直接存储的用户明文相关信息,不安全。用户登录成功之后,服务端生成一个随机字符串,返回给客户端保存。之后客户端每次发请求携带该随机字符串,服务端获取之后比对后台数据。
session本质,指代服务端保存的跟用户信息相关的数据。

学习内容总结
浙公网安备 33010602011771号