Day 57 多对多关系的第三张表创建/forms组件
多对多关系的第三张表创建
全自动
class Book(models.Model):
title = models.CharField(max_length=32)
# 多对多关系字段
author = models.ManyToManyField(to='Authors')
class Authors(models.Model):
name = models.CharField(max_length=32)
好处: 至始至终你都没有操作第三张表,全部都是由orm自动帮你创建的
还内置了四个操作第三张表的方法
- add
- remove
- clear
- set
缺点: 自动创建的第三张表修改字段,扩展性比较差
手动建立
class Book(models.Model):
title = models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32)
class Book2Author(models.Model):
Book = models.ForeignField(to='Book')
author = models.ForeignField(to='Authors')
create_time = models.DateField(auto_now_add=True)
好处: 第三张表中字段个数和字段名称全都可以自己定义
缺点: 不再支持orm跨表查询,不再由正反向的概念,也没有四个操作第三张表的方法
半自动(推荐)
class Book(models.Model):
title = models.CharField(max_length=32)
authors = models.ManyToManyField(to='Authors', through='Book2Author', through_fields=('book', 'authors'))
'''
当你的ManyToManyField只有一个参数to的情况下,orm会自动帮你创建第三张表
如果你加了through和through_fields,那么orm就不会自动帮你创建第三张表了,但是它会在内部帮你维护关系,让你能够继续使用orm的跨表查询
through 自己指定第三张表
through_fields 自己指定第三张表关系中,到底哪两个字段维护着表与表之间的多对多关系
'''
class Author(models.Model):
name = models.CharField(max_length=32)
# books = models.ManyToManyField(to='Book', through='Book2Author', through_field=('authors', 'book'))
class Book2Author(models.Model):
book = models.ForeignKey(to='Book')
authors = models.ForeignKey(to='Authors')
好处: 可以任意的添加和修改第三张表中的字段,并且支持orm跨表查询
缺点: 不支持四个操作第三张表的方法
forms组件
当我们需要在许多场景下对用户的输入做校验,比如校验用户是否输入,输入的长度和格式是否正确.如果用户输入的内容有错误就需要在页面上相应的位置显示对应的错误信息
我们可以借助Django forms组件实现以上功能
Django forms组件的主要功能如下:
- 生成页面的HTML标签(渲染标签)
- 对用户提交的数据进行校验(校验数据)
- 返回提示错误并依旧保留上次输入的信息(展示信息)
使用forms组件的前提是,你需要提前写一个类
from django import forms
class MyForm(forms.Form):
# username字段,最少三位,最多八位
# error_messages 报错信息修改
# widget 添加类名
username = forms.CharField(max_length=8, min_length=3, label='用户名', initial='默认值',
error_messages={
'max_length': '用户名最长八位',
'min_length': '用户名最短三位',
'required': '用户名不能为空'
}, required=False, widget=forms.widgets.TextInput({'class': 'form-control c1 c2', 'username': 'tiny'})
)
password = forms.CharField(max_length=8, min_length=3, label='密码',
error_messages={
'max_length': '密码最长八位',
'min_length': '密码最短三位',
'require': '密码不能为空'
}, widget=forms.widgets.PasswordInput({'class': 'form-control'})
)
confirm_password = forms.CharField(max_length=8, min_length=3, label='确认密码',
error_messages={
'max_length': '确认密码最长八位',
'min_length': '确认密码最短三位',
'require': '确认密码不能为空',
}, widget=forms.widgets.PasswordInput({'class': 'form-control'})
)
email = forms.EmailField(label='邮箱', error_messages={
'required': '邮箱不能为空',
'invalid': '邮箱格式错误',
}, widget=forms.widgets.EmailInput({'class': 'form-control'}))
# reg
def reg(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, 'reg.html', locals())
校验数据
# 1.给写好的类 传字典数据(待校验的数据)
form_obj = views.MyForm({'username': 'jason', 'password': '12', 'email': '123'})
# 2.如何让查看校验的数据是否合法
form_obj.is_valid() # 返回True或者False
# 3.如何查看不符合规则的字段及错误的理由
form_obj.errors # 一个字典
# 4.如何查看符合校验规则的数据
form_obj.cleaned_data # 一个字典
# 5.forms组件中 定义的字段都是必须传值的 不能少传
form_obj = views.MyForm({'username': 'jason', 'password': '12345',})
form_obj.is_valid() # 返回False
# {'email': ['This field is required.']}
# 6.forms组件只会校验forms类中定义的字段 如果你多传了 不会有任何影响(这就是reg方法中POST请求时再次生成MyForm对象时放入request.POST的原因)
渲染标签
<!--form组件渲染标签方式1:封装程度高,不推荐使用,但是可以用在本地测试-->
{#{{ form_obj.as_p }}#}
{#{{ form_obj.as_ul }}#}
{#{{ form_obj.as_table }}#}
<!--form组件渲染标签方式2:不推荐使用,用起来麻烦-->
{#{{ form_obj.username.label }}{{ form_obj.username }}#}
{#{{ form_obj.password.label }}{{ form_obj.password }}#}
{#{{ form_obj.email.label }}{{ form_obj.email }}#}
<!--form组件渲染标签方式3:推荐使用-->
{% for forms in form_obj %}
<p>{{ forms.label }}{{ forms }}</p>
{% endfor %}
展示信息
<!--如何告诉浏览器不做校验 form表单中加一个novalidate参数即可-->
<form action="" method="post" novalidate>
{% for forms in form_obj %}
<p>
{# 注意这里已经是forms了#}
{{ forms.label }}{{ forms }}
<span>{{ forms.errors.0 }}</span>
</p>
{% endfor %}
<input type="submit">
</form>
钩子函数
当你觉得以上的所有校验还是不能够满足你的需求,你可以考虑使用钩子函数,函数内部你可以写任意的校验代码
局部钩子
校验单个字段
def clean_username(self):
username = self.clean_data.get('username')
if '666' in username:
# 给username所对应的框展示错误信息
self.add_error('username', '光喊666是不行的')
# 源码会捕获异常
# raise ValidationError('到底对不对呀')
return username # 返回key值
全局钩子
校验多个字段
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
其他字段及参数
# gender = forms.ChoiceField(
# choices=((1, "男"), (2, "女"), (3, "保密")),
# label="性别",
# initial=3,
# widget=forms.widgets.RadioSelect()
# )
#
# 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()
# )
# keep = forms.ChoiceField(
# label="是否记住密码",
# initial="checked",
# widget=forms.widgets.CheckboxInput()
# )
# hobby2 = forms.MultipleChoiceField(
# choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
# label="爱好",
# initial=[1, 3],
# widget=forms.widgets.CheckboxSelectMultiple()
# )