多对多第三张表的创建方式 和 forms组件的使用
一、多对多第三张表的创建
- 共有三种创建方式:全自动,纯手撸,半自动
- 推荐使用半自动方式
1. 全自动方式
(1)实现代码
class Book(models.Model):
title = models.CharField(max_length=32)
# 多对多关系字段
authors = models.ManyToManyField(to='Authors')
class Authors(models.Model):
name = models.CharField(max_length=32)
(2)优点和不足
优点
- 全部都是由orm自动帮你创建的
- 还内置了四个操作第三张表的方法:
add, remove, set, clear
不足:
- 自动创建的第三张表无法扩展个修改字段,表的扩展性较差
2. 纯手撸方式(了解)
(1)实现代码
class Book(models.Model):
title = models.CharField(max_length=32)
class Authors(models.Model):
name = models.CharField(max_length=32)
class Book2Authors(models.Model):
book = models.ForeignKey(to="Book")
author = models.ForeignKey(to="Authors")
create_time = models.DateField(auto_now_add = True)
(2)优点和不足
优点:
- 第三张表中字段个数和字段名称全都可以自己定义
不足:
- 不再支持orm跨表查询
- 不再有正反向的概念
- 不支持内置了四个操作第三张表的方法:
add, remove, set, clear
3. 半自动方式(推荐使用)
(1)实现代码
- 注意:代码中的多行注释部分
class Book(models.Model):
title = models.CharField(max_length=32)
# 多对多关系字段
authors = models.ManyToManyField(to='Authors',through='Book_Author',through_fields=("book","authors"))
"""
当你的ManyToManyField只有一个参数to的情况下 orm会自动帮你创建第三张表
如果你加了through和through_fields那么orm就不会自动帮你创建第三张表 但是它会在内部帮你维护关系 让你能够继续使用orm的跨表查询
through 自己指定第三张关系表
through_fields 自己指定第三张关系表中,到底哪两个字段维护者表与表之间的多对多关系,且through_fields在哪个类中,其括号内的第一个参数就是对应该类的第三张表中的哪个外键字段。(如Book类中,through_field括号内的第一个参数就是第三张表的book外键字段)
"""
class Authors(models.Model):
name = models.CharField(max_length=32)
# 多对多关系字段 等价与Book表中的authors字段
# books = models.ManyToManyField(to='Book',through='Book_Author', through_fields=("authors","book"))
# 该表中可以由任意多的外键字段
# 可以扩展任意的字段
class Book_Author(models.Model):
book = models.ForeignKey(to='Book')
authors = models.ForeignKey(to='Authors')
(2)优点和不足
- 优点:
- 可以任意的添加和修改第三张表中的字段
- 并且支持orm跨表查询
- 不足:
- 不支持内置了四个操作第三张表的方法:
add, remove, set, clear
- 不支持内置了四个操作第三张表的方法:
二、forms组件
- forms组件是django自带的一个组件,用来校验前端form表单中用户提交的数据是否符合我们指定的格式。
1. forms组件的3大作用
渲染标签:通过forms组件语法,创建
input标签校验数据:将数据转递给后端做数据校验。(当使用forms组件时,前端浏览器会自动识别,并在前端会自动做一次校验。但是前端的校验基本没有意义,一般关闭前端校验。方法是:在form表单中加一个
novalidate参数即可)展示信息:如果数据有错误,展示错误信息
2. 渲染标签
- 继承
forms.Form的类,通过实例化出一个对象,传到前端后,在前端可以将其内定义的属性直接渲染成用户输入的标签。但是不会帮你生成一个提交按钮,因此需要你手动创建。
(1)前端渲染标签的3种方式
- 前端中渲染标签的方式推荐使用第三种
<!--forms组件渲染标签方式1:封装程度态高 不推荐使用 但是可以用在本地测试-->
{{ form_obj.as_p }} <!--自动渲染所有input框 -->
{{ form_obj.as_ul }}
{{ form_obj.as_table }}
<!-- forms组件渲染标签方式2:不推荐使用 写起来太烦了-->
{{ form_obj.username.label }}{{ form_obj.username }}
{{ form_obj.username.label }}{{ form_obj.password }}
{{ form_obj.username.label }}{{ form_obj.email }}
<!--**********forms组件渲染标签方式3:推荐使用 *******-->
{% for form in form_obj %}
<p>{{ form.label }}{{ form }}</p> <!--form 等价于你方式2中的对象点字段名-->
{% endfor %}
(2)后端写法
- 步骤
- 先导入forms组件
- 创建一个继承
forms.Form的类 - 在功能函数中实例化一个空的forms类的对象
form_obj = MyForm() - 判断前端的提交方式为POST时,再生成一个必须与空对象同名的的forms类的对象
form_obj = MyForm(request.POST) - 给前端返回数据
- 后端实例
from django import forms
class MyForm(forms.Form):
# username字段 最少三位 最多八位
username = forms.CharField(max_length=8,min_length=3)
# password字段 最少三位 最多八位
password = forms.CharField(max_length=8,min_length=3)
# email字段 必须是邮箱格式
email = forms.EmailField()
def index(request):
# 渲染标签 第一步 需要生成一个空的forms类的对象
form_obj = MyForm()
# 如何校验前端用户传入的数据
if request.method == 'POST':
# 获取用户的数据 request.POST中 forms组件校验数据
form_obj = MyForm(request.POST) # 改变量名一定要跟上面的form_obj变量名一致
if form_obj.is_valid(): # 研究forms组件入口就是is_valid()
print(form_obj.cleaned_data)
return HttpResponse('数据全部OK')
# 直接将生成的对象 传递给前端页面
return render(request,'index.html',locals())
(3)forms基本使用实例:
# **************后端:*****************
from django import forms
class MyForm(forms.Form):
# username字段 最少三位 最多八位
username = forms.CharField(max_length=8,min_length=3)
# password字段 最少三位 最多八位
password = forms.CharField(max_length=8,min_length=3)
# email字段 必须是邮箱格式
email = forms.EmailField()
def index(request):
# 渲染标签 第一步 需要生成一个空的forms类的对象
form_obj = MyForm()
# 如何校验前端用户传入的数据
if request.method == 'POST':
# 获取用户的数据 request.POST中 forms组件校验数据
form_obj = MyForm(request.POST) # 改变量名一定要跟上面的form_obj变量名一致
if form_obj.is_valid(): # 研究forms组件入口就是is_valid()
print(form_obj.cleaned_data)
return HttpResponse('数据全部OK')
# 直接将生成的对象 传递给前端页面
return render(request,'index.html',locals())
# *******************前端*****************
# index.html文件中
<body>
<form action="" method="post" novalidate>
{% for forms in form_obj %}
<p>
<!--渲染标签-->
{{ forms.label }}{{ forms }}
<!--展示错误信息-->
<span>{{ forms.errors.0 }}</span>
</p>
{% endfor %}
<input type="submit">
</form>
</body>
3. 校验数据
数据的校验通常前后端都必须有,但是前端的校验可有可无,并且弱不禁。后端的校验必须要有,并且必须非常的全面
如何告诉浏览器不做校验 form表单中加一个
novalidate参数即可这里的校验数据是我们查看forms组件校验数据的原理。真正的校验数据其实在定义类时,各字段括号里的属性就是对其的校验条件。有了校验条件后,forms组件内部会自动帮我们校验。
forms组件的校验数据中有几个方法
forms对象 = MyForm({'username':'jason','password':'12','email':'123'}):实例化forms对象。在实际开发中,括号里直接放request.POST即可。forms对象.is_valid():查看校验的数据是否合法。只有当你的数据全部符合校验规则的情况下,结果才是True,否则都为Falseforms对象.errors:查看错误信息,不加括号(错误信息包含不符合规则的字段及其错误的理由)(数据格式:{字段名:[错误信息]})forms对象.cleaned_data:查看符合校验规则的数据,不加括号。(数据格式:{字段名:字段值})- 在实例化forms对象时,给类传的参数是个大字典,大字典内必须包含类中定义的那些属性字段。
4. 展示错误信息
- 即在渲染标签时,一起书写代码
<form action="" method="post" novalidate>
{% for forms in form_obj %}
<p>
<!--渲染标签-->
{{ forms.label }}{{ forms }}
<!--展示错误信息-->
<span>{{ forms.errors.0 }}</span>
</p>
{% endfor %}
<input type="submit">
</form>
三、forms组件的校验条件和错误信息
- forms组件的对前端发送来的数据的进行校验的条件和向前端发送的中文的错误信息都是在定义forms类时书写的。
1. 校验条件和错误信息的书写
- 必须掌握的参数
label # input对应的提示信息,不指定该参数,则默认是首字母大写的字段名
initial # input框默认值
required # 默认为True控制字段是否必填
error_messages # 等于一个大字典。里面填写对应的校验条件的自定义错误信息
validators # 等于一个列表。里面放的是正则校验条件
widget # 给input框设置样式及属性,不写则默认input框的type属性值是text
# 等价于下面的写法
widget=forms.widgets.TextInput({'class':'form-control c1 c2','username':'jason'})
# 等价于上面的写法
widget=forms.widgets.TextInput(attrs={'class':'form-control c1 c2','username':'jason'})
- 实例
from django import forms
from django.core.validators import RegexValidator # 使用正则校验数据
from django.forms import widgets # 给input框设置样式及属性
class MyForm(forms.Form):
# username字段 最少三位 最多八位
username = forms.CharField(
max_length=8,
min_length=3,
label='用户名', # input框对应的提示信息
initial='默认值', # 给input框设置默认值
required=False, # 控制字段是否必填(默认为True)
error_messages={
'max_length':'用户名最长八位',
'min_length':'用户名最短三位',
'required':'用户名不能为空'
},
validators=[
RegexValidator(r'^[0-9]+$', '请输入数字'),
RegexValidator(r'^159[0-9]+$', '数字必须以159开头'),
], # 使用正则校验数据(前面是正则表达式,后面直接是报错信息)
widget=forms.widgets.TextInput({'class':'form-control c1 c2','username':'jason'}) # 给input框设置样式及属性,并且input框的type设为text
)
# password字段 最少三位 最多八位
password = forms.CharField(
max_length=8,
min_length=3,
label='密码',
error_messages={
'max_length': '密码最长八位',
'min_length': '密码最短三位',
'required': '密码不能为空'
},
widget=forms.widgets.PasswordInput() # 给input框设置样式及属性,并且input框的type设为password
)
# 确认密码
confirm_password = forms.CharField(
max_length=8,
min_length=3,
label='确认密码',
error_messages={
'max_length': '确认密码最长八位',
'min_length': '确认密码最短三位',
'required': '确认密码不能为空'
},
)
# email字段 必须是邮箱格式
email = forms.EmailField(
label='邮箱',
error_messages={
'required':'邮箱不能为空',
'invalid':'邮箱格式错误'
})
2. 其他校验数据的方式
(1)HOOK(钩子函数)
注意:钩子函数写在自定义的forms类中
当你觉得1中的校验条件还不能够满足你的需求,你可以考虑使用钩子函数
是一个函数,函数体内你可以写任意的校验代码
分为局部钩子和全局钩子
- 局部钩子:只能钩取单个字段进行校验
- 全局钩子:可同时钩取多个字段进行校验
1. 局部钩子
语法:
def clean_字段1(self):
# 拿到要校验的字段数据
变量(接收字段数据) = self.cleaned_data.get('字段1') # 书写校验代码code # 最后一定要返回字段(有钩有还),不返回也没事,后端会自动检验,帮你返回。建议规范写法
return 字段1
局部钩子实例
def clean_username(self):
username = self.cleaned_data.get('username')
if '666' in username:
# 给username所对应的框展示错误信息
# self.add_error('username','光喊666是不行的')
raise ValidationError('光喊666是不行的') # 直接向前端发送错误信息,等价于上一行的self.add_error('username','光喊666是不行的')
# 最后一定要将字段返回
return username
2. 全局钩子
语法加实例:
# 校验密码 确认密码是否一致 全局钩子
def clean(self):
password = self.cleaned_data.get("password")
confirm_password = self.cleaned_data.get("confirm_password")
if not password == confirm_password:
# 给username所对应的框展示错误信息
self.add_error('confirm_password','两次密码不一致')
# 将全局的数据返回
return self.cleaned_data
(2)选择类的input框
# 1. type=radio
gender = forms.ChoiceField(
choices=((1, "男"), (2, "女"), (3, "保密")),
label="性别",
initial=3,
widget=forms.widgets.RadioSelect()
)
# 2. type=checkbox(单选)
keep = forms.ChoiceField(
label="是否记住密码",
initial="checked",
widget=forms.widgets.CheckboxInput()
)
# 3. type=checkbox(多选)
hobby2 = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=[1, 3],
widget=forms.widgets.CheckboxSelectMultiple()
)
# 4. select选择框(单选)
hobby = forms.ChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=3,
widget=forms.widgets.Select()
)
# 5. select选择框(多选)
hobby1 = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=[1, 3],
widget=forms.widgets.SelectMultiple()
)
(3)其他约束条件
多对多第三张表的创建方式 和 forms组件的使用的更多相关文章
- 多对多关系表的创建方式、forms组件
目录 多对多关系表的三种创建方式 1.全自动,Django自动创建 2.纯手撸 3.半自动(推荐使用) forms组件 小例子 forms组件 校验器 钩子函数 局部钩子 全局钩子 forms组件常用 ...
- 多对多三种创建方式、forms组件、cookies与session
多对多三种创建方式.forms组件.cookies与session 一.多对多三种创建方式 1.全自动 # 优势:不需要你手动创建第三张表 # 不足:由于第三张表不是你手动创建的,也就意味着第三张表字 ...
- Django框架(十一)-- 补充:inclusion_tag、defer、only、choice、事务、创建多对多的第三张表、mvc和mtv模式
一.inclusion_tag 1.作用 用于生成HTML片段,是数据由参数传入而变成动态 2.使用 # 1.app下新建一个模块,templatetags # 2.创建一个py文件(mytag.py ...
- Django框架(十二)—— 补充:inclusion_tag、defer、only、choice、事务、创建多对多的第三张表
目录 补充:inclusion_tag.defer.only.choice.事务.创建多对多的第三张表 一.inclusion_tag 1.作用 2.使用 二.defer与only 1.定义 2.使用 ...
- Django学习——图书管理系统图书修改、orm常用和非常用字段(了解)、 orm字段参数(了解)、字段关系(了解)、手动创建第三张表、Meta元信息、原生SQL、Django与ajax(入门)
1 图书管理系统图书修改 1.1 views 修改图书获取id的两种方案 1 <input type="hidden" name="id" value=& ...
- Django:常用字段、手动自动第三张表单、元信息
一.常用字段和非常用字段 二.手动,自动创建第三张表 三.元信息 四.defer和only 一.常用字段和非常用字段 -常用字段 AutoField int自增列,必须填入参数 primary_key ...
- Django之modles 多对多创建第三张表
一.第一种:纯自动创建第三张表 纯自动 class Book(models.Model): title = models.CharField(max_length=32) price = models ...
- Django之多对多表之through第三张表之InlineModelAdmin后台内嵌
话不多说,来看表结构 这里有两个表,一个是阶段表,一个是老师表,一个老师可以带多个阶段,一个阶段也可以由多个老师带,所以是多对多关系 # 阶段表 class Stage(models.Model): ...
- django 多对多自定义第三张表时的注意事项
杂交(自定义第三张表+ManyToManyField) # modles.py class Boy(models.Model): name = models.CharField(max_length= ...
随机推荐
- cisco三层交换为vlan配置dhcp
dhcp(config)#vlan 2 dhcp(config-vlan)#name sales dhcp(config-vlan)#vlan 3 dhcp(config-vlan)#name ...
- dll的封装和使用
背景 在windows平台下,要实现函数的封装,一般采用的是dll动态库的形式 实现了函数的封装就意味着实现过程的隐藏 可以实现跨平台和跨语言的使用 实施步骤 生成dll动态库 在VS中生成新项目,应 ...
- Angular中引入外部js的使用方式
在Angular中我们或许会用到部分外部插件的时候,像Bootstrap,Jquery这些当然我们可以通过Npm安装包的形式引入,但是还有一些其它的js库需要引入的话,我们又应该怎样操作呢? 在这里做 ...
- 【Java 基础】Java 基础索引
Java 基础 注解 [注解]深入理解Java注解类型(@Annotation) [注解]Java注解(1)-基础 [注解]Java注解(2)-运行时框架 [注解]Java注解(3)-源码级框架
- 【0.2】【MySQL】常用监控指标及监控方法(转)
[MySQL]常用监控指标及监控方法 转自:https://www.cnblogs.com/wwcom123/p/10759494.html 对之前生产中使用过的MySQL数据库监控指标做个小结. ...
- javase-整数变量的交换
public class Operator { public static void main(String[] args) { int a=10; int b=20; System.out.prin ...
- 如何让 node 运行 es6 模块文件,及其原理
如何让 node 运行 es6 模块文件,及其原理 最新版的 node 支持最新版 ECMAScript 几乎所有特性,但有一个特性却一直到现在都还没有支持,那就是从 ES2015 开始定义的模块化机 ...
- luogu题解 P1099 【树网的核】树的直径变式+数据结构维护
题目链接: https://www.luogu.org/problemnew/show/P1099 https://www.lydsy.com/JudgeOnline/problem.php?id=1 ...
- qt webengineview 加载本地资源方式
一.如果把资源添加到本地资源qrc库里了,请使用 ui->preview->setUrl(QUrl("qrc:/HelloWorld2.html")): 二.如果没有现 ...
- java实现spark常用算子之join
import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaPairRDD;import org.apache.spa ...