day59
什么是钩子函数?
在程序的执行时 运行到特定的节点让其自动触发并完成响应操作 钩子函数在forms组件中就类似于第二道关卡,能够让我们自定义校验规则!
在forms组件中有两类钩子
1.局部钩子 当你需要给单个字段额外增加校验规则的时候可以使用 ps: 校验用户名是否已存在(只校验单个字段) 2.全局钩子 当你需要给多个字段额外增加校验规则的时候可以使用 ps: 校验密码和确认密码是否一致(校验多个字段)
通过代码实操来学习钩子函数
models:
from django.db import models class User(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() addr = models.CharField(max_length=64) email = models.EmailField(null=True)
urls:
from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), # forms组件钩子函数 url(r'^register/',views.register), ]
views:
from django import forms class MyForm(forms.Form): name = forms.CharField(min_length=3, max_length=8, label='用户名', error_messages={ 'min_length': '用户名最少三位', 'max_length': '用户名最多八位', 'required': '用户名不能为空' } ) age = forms.IntegerField(min_value=0, max_value=150, label='年龄') addr = forms.CharField(max_length=64, label='地址') email = forms.EmailField(label='邮箱', error_messages={ 'required': '邮箱不能为空', 'isvalid': '邮箱格式不正确' } ) def register(request): form_obj = MyForm() # 先生成一个空对象 return render(request, 'register.html', locals()) # 直接返回这个页面
register.html (注册页面):
<body> <div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <form action="" method="post" novalidate> {% for foo in form_obj %} <p> {{ foo.label }} {{ foo }} <span style="color: red">{{ foo.errors.0 }}</span> </p> {% endfor %} <input type="submit"> </form> </div> </div> </div> </body>
如果你想要对用户名密码做二次校验 那么就可以使用到钩子函数
clean_
后面跟想要校验的字段 专门用于对字段添加额外校验规则
ps:
clean_name
钩子函数触发的节点:
钩子函数需要等第一次校验结束后,没有问题,才执行额外校验(钩子函数) 1.第一次校验的数据会放在self.cleaned_data里面 2.self就是当前类产生的对象,就是form_obj
views:
# 局部钩子:校验用户名是否已存在(一次性只能勾一个人) '''钩子函数是数据经过了字段第一层参数校验之后才会执行''' def clean_name(self): # 自动生成的函数名 专门用于对name字段添加额外的校验规则 # 1.先获取用户名 name = self.cleaned_data.get('name') # 从已经经过第一次校验的数据中获取用户名 # 2.判断用户名是否已存在 is_exist = models.User.objects.filter(name=name) if is_exist: # 3.展示提示信息 self.add_error('name', '用户名已存在') # add_error内 第一个放 要设置的字段 第二个放 展示的错误信息 # 最后将你勾上来的name返回回去 return name
在register内添加获取用户数据并返回的操作
def register(request): form_obj = MyForm() if request.method == 'POST': form_obj = MyForm(request.POST) # 获取用户数据(request.POST可以看成是个大字典 直接传入即可 因为根据校验数据的特性 只拿自己需要的 多传的直接忽略) if form_obj.is_valid(): # 校验数据 return HttpResponse("OK") # 数据没问题 正常返回 return render(request, 'register.html', locals())
以上是校验单个字段 使用了局部钩子
那么有时候我们想要校验多个字段 比如说:注册功能中肯定会有输入密码和确认密码的校验
来!上代码:
views:
class MyForm(forms.Form): name = forms.CharField(min_length=3, max_length=8, label='用户名', initial='Jason', error_messages={ 'min_length': '用户名最少三位', 'max_length': '用户名最多八位', 'required': '用户名不能为空' } ) password = forms.CharField(min_length=3, max_length=8, label='密码') # 添加密码校验条件 confirm_password = forms.CharField(min_length=3, max_length=8, label='确认密码') # 添加确认密码校验条件 age = forms.IntegerField(min_value=0, max_value=150, label='年龄') addr = forms.CharField(max_length=64, label='地址') email = forms.EmailField(label='邮箱', error_messages={ 'required': '邮箱不能为空', 'isvalid': '邮箱格式不正确' } )
使用全局钩子
views:
# 全局钩子:校验密码与确认密码是否一致(一次性可以勾多个人) def clean(self): # 一次勾多个 直接使用clean即可 # 1.获取多个字段数据 password = self.cleaned_data.get('password') confirm_password = self.cleaned_data.get('confirm_password') if not password == confirm_password: self.add_error('confirm_password', '两次密码不一致') # 括号内第一个放要设置的字段 第二个放需要提示的信息 # 全局钩子拿的是整个数据 最后将整个数据返回 return self.cleaned_data
我们在类里面编写钩子函数 通过钩子函数添加任意的功能 目前这里只是添加了二次校验做演示
下面是一些froms内常用字段:
min_length
# 最小长度
max_length
# 最大长度
label
# 字段名称
error_messages
# 错误提示
min_value
# 最小值
max_value
# 最大值
initial
# 默认值
validators
# 正则校验器
widget
# 控制渲染出来的标签各项属性
choices
# 选择类型的标签内部对应关系
先导入模块
from django.core.validators import RegexValidator
参数书写方式 如下
widget
使用方法:如何让用户名拥有样式?
效果如下
如何让输入的密码变成密文呢?
widgets.
后面跟什么类型就控制了Input标签type的类型 默认情况下是Text
通过attrs
来控制各项属性
这样我们就能通过widget来让字段具备样式 具备不同的样式特点
效果如下
选择类型的标签内部对应关系
可以直接编写:
hobby = forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), )
也可以从数据库中获取:
def __init__(self, *args, **kwargs): super(MyForm,self).__init__(*args, **kwargs) self.fields['hobby'].choices = models.Classes.objects.all().values_list('id','caption')
单radio值为字符串
gender = forms.ChoiceField( choices=((1, "男"), (2, "女"), (3, "保密")), # 写上对应关系 label="性别", # 写上字段中文名 initial=3, # 写上默认值 widget=forms.widgets.RadioSelect() # Radio单选框 )
效果如下
hobby = forms.ChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=3, widget=forms.widgets.Select() # 下拉单选框 )
效果如下
hobby1 = forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget=forms.widgets.SelectMultiple() # 下拉多选框 )
效果如下
hobby2 = forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget=forms.widgets.CheckboxSelectMultiple() )
效果如下
file = forms.FileField(label='文件', widget=forms.widgets.FileInput())
效果如下
""" 看源码可以提升编程能力(CBV源码) 也可以从中获取出更多的使用方式(JsonResponse) """ def is_valid(self): return self.is_bound and not self.errors # 查看类源码发现只要给类传参self.is_bound肯定是True @property def errors(self): if self._errors is None: self.full_clean() return self._errors # 查看了源码发现self._errors初始化就是None 所以肯定走full_clean方法 # 查看源码发现校验数据的整个过程内部都有异常处理机制 from django.core.exceptions import ValidationError raise ValidationError('用户名不存在 ')
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简介 早期的互联网应用程序都是不保存用户状态的,所有人发送请求返回的都是相同的页面 现如今几乎所有的应用程序都可以保存用户状态!!!>>>:如何实现的??? HTTP协议四大特性之一:无状态 让服务端知道你是谁的方式很单一>>>:携带用户名和密码(身份标识) 每次操作之前都需要输入用户名和密码 当你成功登录之后浏览器会在本地帮你保存用户名和密码 每次操作浏览器自动发送用户名和密码 cookie本质 指代服务端让客户端保存的数据(存储在客户端上与用户信息相关的数据) '''简单的记忆:cookie就是存在客户端的东西''' # session简介 早期的cookie是直接存储的用户明文相关信息 不安全 用户登录成功之后 服务端生成一个随机字符串 返回给客户端保存 之后客户端每次发请求携带该随机字符串 服务端获取之后比对后台数据 eg: 服务端 随机字符串1 用户数据1 随机字符串2 用户数据2 随机字符串3 用户数据3 客户端 随机字符串1、随机字符串2、随机字符串3 session本质 指代服务端保存的跟用户信息相关的数据 '''简单的记忆:session就是存在服务端的东西''' """ 1.session的工作必须依赖于cookie 2.客户端也有权拒绝保存数据 """ ps:针对身份标识问题有很多方式 cookie和session是最基本的 token、jwt...
# 视图函数返回值 return HttpResonse() return render() return redirect() ... 不直接返回对象 而是先用变量名指代 然后操作对象方法 res = HttpResonse() return res res = render() return res res = redirect() return res # 基本使用 res.set_cookie() # 设置 res.COOKIE.get() # 获取 # 有很多视图函数需要添加登录认证 有时候又需要取消登录认证 装饰器 # 用户登录之后跳转到用户登录之前想要访问的页面 提示:利用url问号携带参数 尝试编写
def login_auth(func_name): def inner(request, *args, **kwargs): if request.COOKIES.get('name'): res = func_name(request, *args, **kwargs) return res else: return redirect('/login/') return inner @login_auth def home(request): # if request.COOKIES.get('name'): # return HttpResponse("只有登录的用户才可以查看home页面") # return redirect('/login/') return HttpResponse("只有登录的用户才可以查看home页面") @login_auth def index(request): # if request.COOKIES.get('name'): # return HttpResponse("只有登录的用户才可以查看index页面") # return redirect('/login/') return HttpResponse("只有登录的用户才可以查看index页面") @login_auth def func(request): # if request.COOKIES.get('name'): # return HttpResponse("只有登录的用户才可以查看func页面") # return redirect('/login/') return HttpResponse("只有登录的用户才可以查看func页面")