多对多第三张表的创建方式 和 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= ...
随机推荐
- PJzhang:关闭wps小广告和快速关闭445端口
猫宁!!! kali linux上安装的wps,没有广告,而且轻巧简洁. 如果你在windows上安装wps,除了ppt.word.excel,还会有一个h5的应用,当然,最令人烦扰的当 ...
- Java闭包和回调
Java中的闭包与回调可以通过成员内部类来实现. 简单的来说闭包就是返回一个内部类对象,这个内部类对象包含了外部类对象的信息(因为一个内部类对象定依托于一个外部类对象). 回调就是在闭包的基础上实现的 ...
- pandas中.value_counts()用于统计数据集中的某一列
value_counts()是一种查看表格某列中有多少个不同值的快捷方法,并计算每个不同值有在该列中有多少重复值.value_counts()是Series拥有的方法,一般在DataFrame中使用时 ...
- Java小知识----List复制:浅拷贝与深拷贝
原文地址: https://blog.csdn.net/demonliuhui/article/details/54572908 List浅拷贝 众所周知,list本质上是数组,而数组的是以地址的形式 ...
- python并发编程-进程理论-进程方法-守护进程-互斥锁-01
操作系统发展史(主要的几个阶段) 初始系统 1946年第一台计算机诞生,采用手工操作的方式(用穿孔卡片操作) 同一个房间同一时刻只能运行一个程序,效率极低(操作一两个小时,CPU一两秒可能就运算完了) ...
- Oracle 服务名/实例名,Service_name 和Sid的区别
Service_name 和Sid的区别Service_name:该参数是由oracle8i引进的.在8i以前,使用SID来表示标识数据库的一个实例,但是在Oracle的并行环境中,一个数据库对应多个 ...
- [Nest] 02.nest之控制器
控制器 Controller Nest 的核心概念 模块 Module 控制器 Controller 服务与依赖注入 Provider Dependency injection 控制器负责处理应用的特 ...
- O004、启动第一个KVM虚机
参考https://www.cnblogs.com/CloudMan6/p/5249270.html 本节演示如何使用 virt-manager 启动 KVM 虚机, 提前下载一个 cirro ...
- 【学习总结】Markdown 使用-表格及其居中等格式
参考: Learning-Markdown (Markdown 入门参考)-表格 Markdown 注:主要是github中的使用 要点: 不管是哪种方式,第一行为表头,第二行为分割表头和主体部分,第 ...
- css简单动画(transition属性)
一.对transition属性的认识 1.transition 属性是一个简写属性,可用于设置四个过渡属性:transition-property 过渡效果的 CSS 属性的名称(height ...