目录 表模型类多对多关系的三种创建方式 django forms 组件 登录功能手写推理过程 整段代码可以放过来 forms 组件使用 forms 后端定义规则并校验结果 forms 前端渲染标签组件 forms 组件其他知识点(服务器端的) 在 python console 测试 forms 组件数据校验规则 其他几个常见字段类型 forms 所有内置字段类型 forms 组件字段常见参数 forms 组件钩子函数 forms 校验最终版 forms 校验源码分析 cookie 与 session cookie 工作原理 session 工作原理 如何操作 cookie 服务端常见的 cookie 操作 小练习 如何操作 session 设置 session 获取 session 删除 session 设置 session 超时时间 关系表可能还会有一个关系创建时间字段(这条关联记录什么时候添加的) 全自动:django 自动创建第三张表 利用 ManyToManyField() 优点:不需要手动创建第三张表 不足:由于第三张表不是你手动创建的,也就意味着第三张表中字段是固定的,无法做扩展 纯手动:手动创建第三张表 优点:第三张表可以任意扩展字段 不足:查询不方便,基于双下划线、对象的反向查询都不支持了 半自动:指定关联表和第三张表的关系 优点:可以自定义字段,依旧支持基于双下划线、对象的反向查询 # 1.第一种 django orm 自动帮我们创建 class Book(models.Model): name = models.CharField(max_length=32) authors = models.ManyToManyField(to='Author') class Author(models.Model): name = models.CharField(max_length=32) # 2.第二种纯手动创建第三张表 class Book(models.Model): name = models.CharField(max_length=32) class Author(models.Model): name = models.CharField(max_length=32) class Book2Author(models.Model): book = models.ForeignKey(to='Book') author = models.ForeignKey(to='Author') info = models.CharField(max_length=32) # 3.第三种半自动创建第三张表(可扩展性高,并且能够符合orm查询) class Book(models.Model): name = models.CharField(max_length=32) # 第三种创建表的方式 authors = models.ManyToManyField(to='Author', through='Book2Author', through_fields=('book', 'author')) # through 告诉 django orm 书籍表和作者表的多对多关系是通过 Book2Author 来记录的 # through fields 告诉 django orm 记录关系时用过 Book2Author 表中的 book 字段 和 author字段 来记录的(第一个参数 book 是 关联表查 book 所依赖的字段) # 但是,多对多字段的 add set remove clear 四个方法就用不了了 class Author(models.Model): name = models.CharField(max_length=32) # book = models.ManyToManyField(to='Book',through='Book2Author',through_fields=('author','book')) class Book2Author(models.Model): book = models.ForeignKey(to='Book') author = models.ForeignKey(to='Author') info = models.CharField(max_length=32) 科普:前后端都可以校验数据,前端可以不做,但是后端必须要做! 在页面上搭框架 >>> 渲染页面 后端接收数据并校验 >>> 校验数据 展示错误信息给前端页面 >>> 展示信息(span 标签写报错信息) 整段代码可以放过来 forms 组件能够直接帮你完成上面的三步操作 还能给你把校验不通过的数据保留在表单中 在前端渲染标签组件 支持在前端与后端进行双重数据校验 自定义展示错误提示信息 这一般是用在前后端不分离项目中的 forms 后端定义规则并校验结果 写一个继承了 forms.Form 的类 写校验规则 写法和写模型表类极其相似,但是 forms 组件的字段有约束,模型表类的字段没有约束 from django import forms class LoginForm(forms.Form): username = forms.CharField(max_length=8,min_length=3) # 用户名最长八位最短三位 password = forms.CharField(max_length=8,min_length=5) # 密码最长八位最短五位 email = forms.EmailField() # email必须是邮箱格式 基本使用 将需要校验的数据,以字典的方式传递给自定义的类,实例化产生对象 form_obj = views.LoginForm({'username':'jason','password':'123','email':'123'}) 如何查看数据是否全部合法 form_obj.is_valid() ,只有所有的数据都符合要求 才会是True False 如何查看错误原因 form_obj.errors { 'password': ['Ensure this value has at least 5 characters (it has 3).'], 'email': ['Enter a valid email address.'] } 如何查看校验通过的数据 form_obj.cleaned_data {'username': 'jason'} forms 前端渲染标签组件 渲染页面

第一种渲染页面的方式(封装程度太高 一般只用于本地测试 通常不适用)

{{ form_obj.as_p }} {{ form_obj.as_ul }} {{ form_obj.as_table }}

第二种渲染页面的方式(可扩展性较高 书写麻烦)

{{ form_obj.username.label }}{{ form_obj.username }}

{{ form_obj.password.label }}{{ form_obj.password }}

{{ form_obj.email.label }}{{ form_obj.email }}

第三种渲染页面的方式(推荐)

{% for foo in form_obj %}

{{ foo.label }}{{ foo }}

{% endfor %} 展示错误信息 默认是 ul 套 li ,可能有多个报错,我们索引取 0,取消它的 ul li 标签嵌套 {% for foo in form_obj %}

{{ foo.label }}:{{ foo }} {{ foo.errors.0 }}

{% endfor %} 注意事项 forms 组件在帮你渲染页面的时候 只会渲染获取用户输入的标签 提交按钮需要你手动添加 input 框的 label 注释 不指定的情况下 默认用的是类中 字段的首字母大写 在 python console 测试 类似于django 里的 测试环境(就不需要再自己手动写测试文件的那堆配置了) 校验数据 Form对象 实例化 参数 是一个大字典 forms 组件数据校验规则 从上往下依次取值校验 校验通过的放到 cleaned_data 校验失败的放到 errors 注意: form 中所有的字段默认都是必须传值的(required=True) 只要有一个字段不通过,form_obj.is_valid() 的结果就是 False,所有字段都校验通过了才会返回 True 校验数据的时候可以多传(多传的数据不会做任何的校验 >> > 不会影响 form 校验规则) 其他几个常见字段类型 # 单选的radio框 gender = forms.ChoiceField( choices=((1, "男"), (2, "女"), (3, "保密")), label="性别", initial=3, widget=forms.widgets.RadioSelect() ) # 单选select hobby = forms.ChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=3, widget=forms.widgets.Select() ) # 多选的select框 hobby1 = forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget=forms.widgets.SelectMultiple() ) # 单选的checkbox keep = forms.ChoiceField( label="是否记住密码", initial="checked", widget=forms.widgets.CheckboxInput() ) # 多选的checkbox hobby2 = forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget=forms.widgets.CheckboxSelectMultiple() ) phone = forms.CharField( validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')], ) forms 所有内置字段类型 Field required=True, 是否允许为空 widget=None, HTML插件 label=None, 用于生成Label标签或显示内容 initial=None, 初始值 help_text='', 帮助信息(在标签旁边显示) error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'} validators=[], 自定义验证规则 localize=False, 是否支持本地化 disabled=False, 是否可以编辑 label_suffix=None Label内容后缀 CharField(Field) max_length=None, 最大长度 min_length=None, 最小长度 strip=True 是否移除用户输入空白 IntegerField(Field) max_value=None, 最大值 min_value=None, 最小值 FloatField(IntegerField) ... DecimalField(IntegerField) max_value=None, 最大值 min_value=None, 最小值 max_digits=None, 总长度 decimal_places=None, 小数位长度 BaseTemporalField(Field) input_formats=None 时间格式化 DateField(BaseTemporalField) 格式:2015-09-01 TimeField(BaseTemporalField) 格式:11:12 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12 DurationField(Field) 时间间隔:%d %H:%M:%S.%f ... RegexField(CharField) regex, 自定制正则表达式 max_length=None, 最大长度 min_length=None, 最小长度 error_message=None, 忽略,错误信息使用 error_messages={'invalid': '...'} EmailField(CharField) ... FileField(Field) allow_empty_file=False 是否允许空文件 ImageField(FileField) ... 注:需要PIL模块,pip3 install Pillow 以上两个字典使用时,需要注意两点: - form表单中 enctype="multipart/form-data" - view函数中 obj = MyForm(request.POST, request.FILES) URLField(Field) ... BooleanField(Field) ... NullBooleanField(BooleanField) ... ChoiceField(Field) ... choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),) required=True, 是否必填 widget=None, 插件,默认select插件 label=None, Label内容 initial=None, 初始值 help_text='', 帮助提示 ModelChoiceField(ChoiceField) ... django.forms.models.ModelChoiceField queryset, # 查询数据库中的数据 empty_label="---------", # 默认空显示内容 to_field_name=None, # HTML中value的值对应的字段 limit_choices_to=None # ModelForm中对queryset二次筛选 ModelMultipleChoiceField(ModelChoiceField) ... django.forms.models.ModelMultipleChoiceField TypedChoiceField(ChoiceField) coerce = lambda val: val 对选中的值进行一次转换 empty_value= '' 空值的默认值 MultipleChoiceField(ChoiceField) ... TypedMultipleChoiceField(MultipleChoiceField) coerce = lambda val: val 对选中的每一个值进行一次转换 empty_value= '' 空值的默认值 ComboField(Field) fields=() 使用多个验证,如下:即验证最大长度20,又验证邮箱格式 fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),]) MultiValueField(Field) PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用 SplitDateTimeField(MultiValueField) input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y'] input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'] FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中 path, 文件夹路径 match=None, 正则匹配 recursive=False, 递归下面的文件夹 allow_files=True, 允许文件 allow_folders=False, 允许文件夹 required=True, widget=None, label=None, initial=None, help_text='' GenericIPAddressField protocol='both', both,ipv4,ipv6支持的IP格式 unpack_ipv4=False 解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用 SlugField(CharField) 数字,字母,下划线,减号(连字符) ... UUIDField(CharField) uuid类型 forms 组件字段常见参数 # 基本都渲染成 HTML 标签的属性(H5 对这些属性有支持) max_length 最大长度 min_length 最小长度 required 是否必填 label 注释信息 initial 初始值(对应的是 value) error_messages 报错信息 widget 控制标签属性和样式 。。。 前端取消浏览器校验 给 form 标签加个 自定义属性 novalidate 即可(
) forms 组件校验数据数据自带保留数据功能,让用户可以基于原来的数据做修改(不合法数据依旧保留在页面的表单中) forms 组件 在后端的这套校验功能依旧生效 error_messages 定义中文报错提示 required 允许字段不填 required=False 使用正则来约束 from django import forms from django.core.validators import RegexValidator class LoginForm(forms.Form): password = forms.CharField( max_length=8, # 密码最长八位最短五位 min_length=5, label='密码', error_messages={ 'max_length': '密码最大八位', 'min_length': '密码最小五位', 'required': '密码不能为空' }, required=False, validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')] ) # 其他代码.... 密码密文 指定渲染标签的 class 等属性(应用样式) ***** from django import forms from django.forms import widgets class LoginForm(forms.Form): username = forms.CharField(max_length=8, min_length=3, label='用户名', initial='tankdsb', error_messages={ 'max_length': '用户名最大八位', 'min_length': '用户名最小三位', 'required': '用户名不能为空' }, widget=widgets.TextInput() ) # 用户名最长八位最短三位 password = forms.CharField(max_length=8, min_length=5, label='密码', error_messages={ 'max_length': '密码最大八位', 'min_length': '密码最小五位', 'required': '密码不能为空' }, widget=widgets.PasswordInput(attrs={'class': 'form-control c1 c2', 'username': 'jason'}) # 指定渲染出来标签的属性(可以指定 class 配合 bootstrap 使用) ) # 密码最长八位最短五位 渲染在页面上的 HTML forms 组件钩子函数 可以自定义校验规则(定制化业务需求) Hook 钩子 全局钩子 针对多个字段作额外的校验 校验用户两次密码(注册)是否一致 # 全局钩子(针对多个字段做额外的校验) 校验用户两次密码是否一致 class LoginForm(forms.Form): def clean(self): 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 局部钩子 针对某一个字段作额外校验(一定要有一个返回值(源码里面要用到)) # 用法:在自定义的form类中书写方法即可 # 局部钩子(针对某一个字段做额外的校验) 校验用户名中不能包含666 一旦包含 提示 class LoginForm(forms.Form): def clean_username(self): username = self.cleaned_data.get('username') if '666' in username: self.add_error('username','光喊666是不行的 你得自己上') return username forms 校验最终版 后端 forms 如何指定并校验(前端渲染基本就上面的固定写法,连样式都是在后端指定上 class 或者直接指定属性加去的) views.py from django.shortcuts import render, HttpResponse, redirect from django.core.exceptions import ValidationError # Create your views here. def login(request): errors = {'username': '', 'password': ''} if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') if 'jpm' in username: errors['username'] = '不符合社会主义核心价值观' if len(password) < 3: errors['password'] = '太短了 不安全!' return render(request, 'login.html', locals()) from django import forms from django.core.validators import RegexValidator from django.forms import widgets class LoginForm(forms.Form): username = forms.CharField(max_length=8, min_length=3, label='用户名', initial='tankdsb', error_messages={ 'max_length': '用户名最大八位', 'min_length': '用户名最小三位', 'required': '用户名不能为空' }, widget=widgets.TextInput() ) # 用户名最长八位最短三位 password = forms.CharField(max_length=8, min_length=5, label='密码', error_messages={ 'max_length': '密码最大八位', 'min_length': '密码最小五位', 'required': '密码不能为空' }, widget=widgets.PasswordInput(attrs={'class': 'form-control c1 c2', 'username': 'jason'}) ) # 密码最长八位最短五位 confirm_password = forms.CharField(max_length=8, min_length=5, label='确认密码', error_messages={ 'max_length': '确认密码最大八位', 'min_length': '确认密码最小五位', 'required': '确认密码不能为空' }, required=False, validators=[