Django框架(十)--ORM多对多关联关系三种创建方式、form组件
多对多的三种创建方式
1.全自动(就是平常我们创建表多对多关系的方式)
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8,decimal_places=2)
authors = models.ManyToManyField(to='Author') class Author(models.Model):
name = models.CharField(max_length=32)
优势:不需要你手动创建第三张表
不足:由于第三张表不是你手动创建的,也就意味着第三张表字段是固定的无法做扩展
2.纯手动(手动建关系表)
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8,decimal_places=2) class Author(models.Model):
name = models.CharField(max_length=32) class Book2Author(models.Model):
book = models.ForeignKey(to='Book')
author = models.ForeignKey(to='Author')
create_time = models.DateField(auto_now_add=True)
优势:第三张可以任意的扩展字段
不足:orm查询不方便
3.半自动(推荐使用)
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8,decimal_places=2)
authors = models.ManyToManyField(to='Author',through='Book2Author',through_fields=('book','author'))
# through 告诉django orm 书籍表和作者表的多对多关系是通过Book2Author来记录的
# through_fields 告诉django orm记录关系时用过Book2Author表中的book字段和author字段来记录的
"""
多对多字段的
add
set
remove
clear不支持
""" class Author(models.Model):
name = models.CharField(max_length=32)
# books = models.ManyToManyField(to='Book', through='Book2Author', through_fields=('author', 'book')) #手动创建的第三张表
class Book2Author(models.Model):
book = models.ForeignKey(to='Book')
author = models.ForeignKey(to='Author')
create_time = models.DateField(auto_now_add=True)
优点:即不用自己创建第三张表了,也可以任意扩展字段了
缺点:这种多对多关系中不能使用add/set/remove/clear这些方法了
form组件
一、什么是form组件,可以干什么
1.forms组件就是一个类,可以检测前端传来的数据,是否合法。
例如,前端传来的邮箱数据,判断邮件格式对不对,用户名中不能以什么开头,等等 >>>校验数据
2.还可以前端页面搭建 >>> 渲染页面
3.展示错误信息 >>> 渲染错误信息
二、form组件的使用
1.使用语法
from django.shortcuts import render, HttpResponse
from django import forms #1.先写一个类,继承Form
class MyForm(forms.Form):
#定义属性,用来校验
#限制最大长度为8,最小长度为3
name = forms.CharField(max_length=8,min_length=3)
pwd = forms.CharField(max_length=8,min_length=3)
#校验邮箱
email = forms.EmailField() #2.在视图函数中使用MyForm来校验数据
#实例化产生对象,将需要校验的数据传入(可以传字典,可以不传),这个数据是前端传递过来的数据
myform = MyForm(request.POST) #3.校验,is_valid如果是true表示校验成功(满足myform里面的所有条件),反之,验证失败
if myform.is_valid():
print(myform.cleaned_data) #打印校验通过的数据
return HttpResponse('校验成功') else:
#校验失败的信息 是一个字典,它是所有错误信息 {错误字段名:[错误原因]}
print(myform.errors) #errors是一个列表,表示每个字段的错误信息
return HttpResponse('校验失败')
方法总结:
clean_data 验证通过的数据
errors 错误数据的对象
errors.as_data 错误数据的信息
注意事项
1.自定义的类中所有的字段默认都是必须要传值的
2.可以额外传入类中没有定义的字段名,forms组件不会去校验,也就是多传没有关系
3.实例化时,传入必须是字典,也可以不传
form_obj = views.LoginForm({'username':'jason','password':'','email':'123@qq.com'})
form_obj.is_valid()
True form_obj = views.LoginForm({'username':'jason','password':''})
form_obj.is_valid()
False form_obj = views.LoginForm({'username':'jason','password':'','email':'123@qq.com','hobby':'read'})
form_obj.is_valid()
True
2.组件的参数及其他操作方式
max_length #代表该字段最长可以为多少
min_length #代表该字段最短可以为多少
error_messages #设置错误信息的属性
required #默认值为True,意思是你前端传来的字段必须有它,没有的话校验失败
label #注释信息label ='用户名' 在前端渲染可以直接对象.label获取值
initial #设置默认值
widget #控制标签属性和样式
widget = widgets.PasswordInput() #你在模板渲染的时候,就会渲染成Input框,password样式
控制标签属性
widget = widgets.PasswordInput(attrs={'class':'form-control c1 c2','username':'jason'})
#例子
pwd = forms.CharField(max_length=8,min_length=3,required=True,label='密码',
error_messages = {'max_length':'最长是8','min_length':'最短是3','required':'这个必须填'}
)
其他操作方式
# 单选的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()
)
三、渲染页面、渲染错误信息
#form组件可以在视图函数中使用,也可以在模板中使用
#视图层
def reg(request):
myform = MyForm()
if request.method == 'POST':
myform = MyForm(request.POST)
print(request.POST) #<QueryDict: {'name': ['小张'], 'pwd': ['123123'], 'email': ['119@qq.com']}> return render(request,'reg.html',locals()) #把myform对象传到前端页面了
#模板层
#第一种渲染方式(封装程度太高,一般只用于本地测试,通常不适用)
<form action='' method='post'>
{{ myform.as_p }}
{{ myform.as_ul }}
{{ myform.as_table}}
<input type='submit' value = '提交'></input>
</form> #第二种渲染页面的方式(可扩展性高,书写麻烦)
<form action='' method='post'>
{{myform.name.label}}:{{myform:name}}
{{myform.pwd.label}}:{{myform:pwd}}
{{myform.email.label}}:{{myform:email}}
<input type='submit' value = '提交'></input>
</form> #第三种渲染方式(推荐)
<form action='' method='post'>
{% for foo in myform %}
<p> {{ foo.lable }} : {{ foo }}
<span>{{ foo.errors.0 }}</span> //注意后面加0 错误信息
</p>
<input type='submit' value = '提交'></input>
</form>
注意事项:
1.forms组件在帮你渲染页面的时候 只会渲染获取用户输入的标签 提交按钮需要你手动添加
2.input框的label注释 不指定的情况下 默认用的类中字段的首字母大写
四、钩子函数(HOOK)
forms组件暴露给用户,可以自定义的校验规则 用法:在自定义的form类中定义一个函数即可
1.局部钩子(针对某一个字段做额外的校验)
名字叫:clean_字段名,内部,取出该字段进行校验,如果通过,将该字段返回,如果失败,抛异常
#函数名:clean_字段名字
def clean_name(self): #self是当前form对象
name = self.cleaned_data.get('name') #从验证通过的数据中再次筛选
if '' in username:
#失败,抛异常,把异常信息以('key','value')形式写入add_error中
self.add_error('name','不能包含666')
#正常,把name返回到clean_data,将name写入clean_data字典中
return name
注意点:
- 这些函数是写在自定义类form里面的,需要校验哪个字段就以 clean_字段名写一个校验函数
- 校验失败,通过add_error('字段名','错误信息'),以{key:value}形式写入到errors字典中
- 校验成功,返回name到clean_data,写入clean_data字典中
2.全局钩子(针对多个字段做额外的校验)
#重写clean方法
def clean(self):
#能走到这步,说明前面的校验已经通过了,下面校验两次两次密码是否输入一致
pwd = self.clean_data.get('pwd')
re_pwd = self.clean_data.get('re_pwd')
if pwd == re_pwd:
#校验成功,直接返回clean_data
return self.clean_data
else:
#校验失败,把错误信息放入errors中
self.add_error('re_pwd','两次密码输入不一致')
或者
#抛出异常
raise ValidationError('两次密码不一致')
注意点:
- 校验失败,有两种写法,抛异常,将异常信息以{'__all__':[value,]}写入errors字典中
- 校验成功,返回clean_data字典
- 抛出异常类型为 ValidationError, from django.core.exceptions import ValidationError导入
钩子错误信息渲染注意点:
- 局部钩子抛出的异常会添加到该字段中的错误信息中,获取错误信息
前端: for循环对象 {{ foo.errors.0}}
- 全局钩子抛出的异常会添加到__all__中,获取错误信息:
后端: myform.errors.get('__all__')[0] 注意先判断myform.errors.get('__all__')是否存在
前端:{{ myform.errors.__all__.0}}
- 能走到这些函数,说明前面的form组件的那些校验都成功了,此时就可以从clean_data中取数据,因为此时clean_data中的数据全部符合,都找的到,而且clean_data是字典比较好取值。
五、完整的form组件校验
1.视图层
from django.shortcuts import render, HttpResponse, redirect # forms组件数据校验的功能
# 第一步:先要继承Form
from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError # 写一个类
class MyForm(forms.Form):
# 定义一个属性,可以用来校验字符串类型
# 限制最大长度是8,最小长度是3
name = forms.CharField(max_length=8, min_length=3, label='用户名'
error_messages={'max_length': '最长是8', 'min_length': '最短是3', 'required': '这个必须填'},
widget=widgets.TextInput(attrs={'class': 'form-control'})) pwd = forms.CharField(max_length=8, min_length=3, required=True, label='密码',
error_messages={'max_length': '最长是8', 'min_length': '最短是3', 'required': '这个必须填'},
widget=widgets.PasswordInput()) re_pwd = forms.CharField(max_length=8, min_length=3, required=True, label='确认密码',
error_messages={'max_length': '最长是8', 'min_length': '最短是3', 'required': '这个必须填'},
widget=widgets.PasswordInput())
# 校验是否是邮箱格式
email = forms.EmailField(label='邮箱', error_messages={'required': '这个必须填', 'invalid': '不符合邮箱格式'}) # aa = forms.CharField(label='选择', error_messages={'required': '这个必须填', 'invalid': '不符合邮箱格式'},widget=widgets.CheckboxInput())
def clean_name(self):
# self:当前form对象
name = self.cleaned_data.get('name')
if name.startswith('sb'):
# 失败,抛异常
raise ValidationError('不能以傻逼开头')
# 正常,把name返回
return name def clean(self):
pwd = self.cleaned_data.get('pwd')
re_pwd = self.cleaned_data.get('re_pwd')
if pwd == re_pwd:
return self.cleaned_data
else:
raise ValidationError('两次密码不一致') def index_form(request):
# 生成对象时(实例化),需要传入要校验的数据(字典)
if request.method == 'GET':
myform = MyForm()
return render(request,'indxe2.html',locals())
elif request.method == 'POST':
myform = MyForm(request.POST)
if myform.is_valid():
# print(myform.cleaned_data) # 验证通过的数据
# models.User.objects.create(name='lqz',pwd='123',re_pwd='123)
myform.cleaned_data.pop('re_pwd')
models.User.objects.create(**myform.cleaned_data)
return redirect('http://www.baidu.com')
else:
all_error = myform.errors.get('__all__')
if all_error:
all_error = all_error[0]
# print(myform.errors.as_data) return render(request, 'indxe3.html', locals())
2.模板层
<form action="" method="post" novalidate>
{% for foo in myform %}
<p>{{ foo.label }}:{{ foo }} <span>{{ foo.errors.0 }}</span></p>
{% endfor %} <input type="submit" value="提交"><span>{{ all_error }}</span>
</form>
Django框架(十)--ORM多对多关联关系三种创建方式、form组件的更多相关文章
- django----多对多三种创建方式 form组件
目录 多对多三种创建方式 全自动 全手动 半自动 form组件 基本使用 form_obj 及 is_valid() 前端渲染方式 取消前端自动校验 正则校验 钩子函数(Hook方法) cleaned ...
- 2019年6月14日 Web框架之Django_07 进阶操作(MTV与MVC、多对多表三种创建方式、前后端传输数据编码格式contentType、ajax、自定义分页器)
摘要 MTV与MVC 多对多表三种创建方式 ajax ,前后端传输数据编码格式contentType 批量插入数据和自定义分页器 一.MVC与MTV MVC(Model View Controller ...
- 多对多的三种创建方式-forms相关组件-钩子函数-cookie与session
多对多的三种创建方式 1.全自动(推荐使用的**) 优势:第三张可以任意的扩展字段 缺点:ORM查询不方便,如果后续字段增加更改时不便添加修改 manyToManyField创建的第三张表属于虚拟的, ...
- ORM中choices参数(重要)、MTV于MVC模型、多对多关系三种创建方式
choices参数(重要) **使用方式
- Django-多对多关系的三种创建方式-forms组件使用-cookie与session-08
目录 表模型类多对多关系的三种创建方式 django forms 组件 登录功能手写推理过程 整段代码可以放过来 forms 组件使用 forms 后端定义规则并校验结果 forms 前端渲染标签组件 ...
- Django多对多表的三种创建方式,MTV与MVC概念
MTV与MVC MTV模型(django): M:模型层(models.py) T:templates V:views MVC模型: M:模型层(models.py) V:视图层(views.py) ...
- 多对多三种创建方式、forms组件、cookies与session
多对多三种创建方式.forms组件.cookies与session 一.多对多三种创建方式 1.全自动 # 优势:不需要你手动创建第三张表 # 不足:由于第三张表不是你手动创建的,也就意味着第三张表字 ...
- Struts2之命名空间与Action的三种创建方式
看到上面的标题,相信大家已经知道我们接下来要探讨的知识了,一共两点:1.package命名空间设置:2.三种Action的创建方式.下面我们开始本篇的内容: 首先我们聊一聊命名空间的知识,namesp ...
- JavaScript 闭包的详细分享(三种创建方式)(附小实例)
JavaScript闭包的详细理解 一.原理:闭包函数--指有权访问私有函数里面的变量和对象还有方法等:通俗的讲就是突破私有函数的作用域,让函数外面能够使用函数里面的变量及方法. 1.第一种创建方式 ...
随机推荐
- haproxy 2.0 dataplaneapi rest api 试用
我们可以基于haproxy 提供的dataplaneapi 动态进行haproxy 配置的修改,增强haproxy的可编程能力,以下是一个简单 的测试,基于docker-compose运行 环境准备 ...
- SDOI 2014 向量集
[SDOI2014]向量集 题目描述 维护一个向量集合,在线支持以下操作: - "A x y (|x|,|y| < =10^8)":加入向量(x,y); - " Q ...
- C++中vector的使用总结
vector简单说明 vector也是一个容器,并且是个顺序容器.顺序容器有可变长数组vector.双向链表list.双端队列deque. 顺序容器的定义,是因为容器元素的位置和他们的值大小无关,也就 ...
- Foxmail: 错误信息::ssl连接错误, errorCode: 5,各种解决方案的大杂烩。
1. 收件数据过多,删除部分邮件可解决 我尝试失败,在foxmail把收件箱全部删完了没解决. 2. 网上最常见的解决方法 https://help.foxmail.com/cgi-bin/hel ...
- 【POJ3414】Pots
本题传送门 本题知识点:宽度优先搜素 + 字符串 题意很简单,如何把用两个杯子,装够到第三个杯子的水. 操作有六种,这样就可以当作是bfs的搜索方向了 // FILL(1) 把第一个杯子装满 // F ...
- CF859E Desk Disorder
传送门 Luogu Solution 好好思考一下,发现人和座位构成的是一个二分图这还用想? 那么这个时候发现其实我们要求的就是这个二分图完全匹配的方案数,考虑在二分图上的一个连通块,如果是树,那么就 ...
- Hadoop FairScheduler
目标 本文档描述FairScheduler,一个允许YARN应用程序公平共享集群资源的调度插件. 概述 公平调度是一个分配资源给所有application的方法,平均来看,是随着时间的进展平等分享资源 ...
- js函数如何传递多个参数
应用场景: 需要根据多个参数来判断该数据所属,从而达到删除或者修改的目的. 比如删除区域下的分组,一个区域可以用多个分组,不同的区域可以有相同的分组,那么如何识别对应的分组呢??? 可以在对应的数据操 ...
- 【C++】C++中的异常解析
异常是程序在执行期间产生的问题.C++ 异常是指在程序运行时发生的特殊情况,比如尝试除以零的操作. 异常提供了一种转移程序控制权的方式.C++ 异常处理涉及到三个关键字:try.catch.throw ...
- Docker学习大纲
Docker学习大纲:https://www.cnblogs.com/CloudMan6/p/7637361.html