目录
表的多对多关系的三种创建方式
查阅框架文档及百度案例:
基于人家已经写好的功能修改
- 先看配置参数
- 前期就是猜, 改几个参数看结果, 看猜的是否对
- 整体修改
全自动创建
class Books(models.Model):
title = models.CharField(max_length=32)
authors = models.ManyToManyField(to='Authors')
class Authors(models.Model):
name = models.CharField(max_length=32)
优点: 1. 不需要手动操作关系表, 全部由orm自动创建; 2. 并且内置了四个操作关系表的方法(add, remove, set, clear)
缺点: 自动创建的关系表无法扩展和修改字段
纯手撸(了解)
class Books(models.Model):
title = models.CharField(max_length=32)
class Authors(models.Model):
name = models.CharField(max_length=32)
class BooksToAuthors(models.Model):
book = models.ForeignKey(to='Books')
author = models.ForeignKey(to='Authors')
create_time = models.DateField(auto_created=True) # 关系表中的扩展字段
优点: 关系表中的字段可以自己定义
缺点: 不再支持orm跨表查询以及四个操作关系表的方法(add, remove, set, clear)
半自动(推荐使用)
class Books(models.Model):
title = models.CharField(max_length=32)
'''
1. 添加through和through_field参数, ManyToManyField就不会再自动创建关系表
2. through设置关系表通过自定义表建立,
3. through_fields设置通过自定义表中的哪两个字段建立多对多关系
4. 在哪张表中声明, 该表在自定义表中对应的字段就放在前面
'''
authors = models.ManyToManyField(to='Authors', through='BooksToAuthors', through_fields=('book', 'author'))
class Authors(models.Model):
name = models.CharField(max_length=32)
# books = models.ManyToManyField(to='Books', through='BooksToAuthors', through_fields=('author', 'book'))
class BooksToAuthors(models.Model):
book = models.ForeignKey(to='Books')
author = models.ForeignKey(to='Authors')
create_time = models.DateField(auto_created=True) # 关系表中的扩展字段
优点: 可以添加和修改关系表中的字段, 并且支持跨表查询
缺点: 不支持四个操作关系表的方法(add, remove, clear, set)
forms组件简介
forms组件的作用
- 渲染标签--- 手动书写html代码获取用户输入
- 校验数据---将数据传递给后端做数据校验
- 展示信息---如果数据有错误, 展示错误信息
# 简易注册信息校验示例
def register(request):
errors_dic = {'username': '', 'password': ''}
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if 'jinpingmei' in username:
errors_dic['username'] = '不符合社会主义核心价值观'
if len(password) <= 3:
errors_dic['password'] = '密码不能少于4位'
return render(request, 'register.html', locals())
'''
<form action="" method="post">
<p>
username: <input type="text" name="username">
<span style="color: red;">{{ errors_dic.username }}</span>
</p>
<p>
password: <input type="text" name="password">
<span style="color: red;">{{ errors_dic.password }}</span>
</p>
<input type="submit">
</form>
'''
forms组件校验数据
-
使用forms组件, 先导入模块:
from django import forms
-
在views.py中自定义MyForm类
class MyForm(forms.Form): username = forms.CharField(max_length=8, min_length=3) # username字段最多8位, 最少3位 password = forms.CharField(max_length=8, min_length=3) email = forms.EmailField() # email字段必须是邮箱格式
-
测试代码的第二种方式: Python Console-->Django Console
from app01 import views form_obj = views.MyForm({'username': 'jason', 'password': '12', 'email': '456'}) # 实例化时参数为字典形式 form_obj.is_valid() # False, 所有数据全部符合校验规则才为True form_obj.errors # {'password': ..., 'email': ['Enter a valid email address.']}, 查看不符合规则的字段及错误信息 form_obj.cleaned_data # {'username': 'jason'}, 查看符合校验规则的数据 ''' 1. forms组件的类中定义的字段默认都必须传值, 不能少传 2. forms组件只会校验类中定义的字段, 如果传入其他字段, 不会有任何影响 '''
forms组件渲染标签
forms组件只会渲染获取用户输入的标签, 不会渲染提交按钮
def index(request):
# 渲染标签, 需先生成一个空的MyForm类的对象
form_obj = MyForm()
return render(request, 'index.html', locals())
forms组件渲染标签方式一
封装程度高, 难扩展, 只用于本地测试
'''
{{ form_obj.as_p }} # 会自动渲染input框和label标签, 由p标签包裹
{{ form_obj.as_ul }} # ..., 由ul标签包裹
{{ form_obj.as_table }} # ..., 渲染到一行上
'''
forms组件渲染标签方式二
组合: form_field_obj为form_obj的属性
class MyForm(forms.Form):
username = forms.CharField(max_length=8, min_length=3, label='用户名')
password = forms.CharField(max_length=8, min_length=3, label='密码')
email = forms.EmailField(label='邮箱')
'''
{% for form_field_obj in form_obj %}
<p>{{ form_field_obj.label }}{{ form_field_obj }}</p>
{% endfor %}
'''
form组件展示信息
数据的校验通常情况前后端都有, 但前端的校验弱不禁风, 所以后端的校验必须要有, 且需非常全面
浏览器识别到使用forms组件时会自动使用前端的校验功能
def index(request):
# 渲染标签, 需先生成一个空的MyForm类的对象
form_obj = MyForm()
if request.method == 'POST': # 前端识别到后端使用forms组件时会自动先进行一轮校验
form_obj = MyForm(request.POST)
if form_obj.is_valid():
return HttpResponse('数据全部OK')
else:
print(form_obj.errors)
return render(request, 'index.html', locals())
设置前端不做校验: <form action="" method="post" novalidate>
def index(request):
# 渲染标签, 需先生成一个空的MyForm类的对象
form_obj = MyForm() # 空对象渲染输入框
if request.method == 'POST':
form_obj = MyForm(request.POST) # form_obj实际渲染用户数据, 两个对象的名称需一致
if form_obj.is_valid():
return HttpResponse('数据全部OK')
return render(request, 'index.html', locals())
'''
<form action="" method="post" novalidate>
{% for form_field_obj in form_obj %}
<p>
{{ form_field_obj.label }}{{ form_field_obj }} # label默认获取到的是类中字段名的首字母大写形式
<span>{{ form_field_obj.errors.0}}</span> # 前端获取报错信息固定写法
</p>
{% endfor %}
<input type="submit">
</form>
'''
class MyForm(forms.Form):
username = forms.CharField(
max_length=8,
min_length=3,
label='用户名',
error_messages={ # 自定义显示报错信息
'max_length': '用户名最长八位',
'min_length': '用户名最短3位',
'required': '用户名不能为空'
}
)
...
email = forms.EmailField(
label='邮箱',
error_messages={
'required': '邮箱不能为空',
'invalid': '邮箱格式错误'
}
)
forms组件自定义校验
正则校验
'''
from django.core.validators import RegexValidator
class MyForm(forms.Form):
username = forms.CharField(
...
...
validators=[
RegexValidator(r'[0-9]+$', '请输入数字!'), # 第一个参数为正则表达式, 第二个参数为校验不通过时的报错信息
RegexValidator(r'^159[0-9]+$', '数字必须以159开头!'),
]
)
'''
钩子函数
书写代码校验字段输入是否合法, 在其他校验合法之后再进行钩子函数代码校验
局部钩子: 校验单个字段
'''
class MyForm(forms.Form):
...
# 校验用户名中不能含有666
def clean_username(self):
username = self.cleaned_data.get('username')
if '666' in username:
self.add_error('username', '光喊666是不行的')
return username
'''
全局钩子: 校验多个字段
'''
class MyForm(forms.Form):
...
# 校验两次输入密码是否一致
def clean(self):
password = self.cleaned_data.get('password')
confirm_password = self.cleaned_data.get('confirm_password')
if not confirm_password == password:
self.add_error('confirm_password', '两次密码不一致!')
return self.cleaned_data
'''
其他字段及参数
需要掌握
'''
class MyForm(forms.Form):
username = forms.CharField(
...,
label='用户名', # input框对应的提示信息
initial='jason', # 初始值
required=False, # 允许字段值为空
widget=forms.widgets.PasswordInput(attrs={'class': 'form-control c1', 'pwd': '123'}), # 密文展示, 给input框设置样式及属性
...
)
# 前端对应标签: <input type="password" ... class="form-control c1" pwd="123" ...>
'''
了解, 会copy即可
'''
radioSelect:
gender = forms.fields.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框, 无需下拉
hobby = 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
hobby = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=[1, 3],
widget=forms.widgets.CheckboxSelectMultiple()
)
'''