Django(九)
目录
Django(九)---多对多的创建方式,horm组件
多对多表创建方式
全自动
通过Django中的orm,只要定义好表类,orm就对自动创建多对多的表关系,自动建立第三张表,并且还可以通过add
remove
set
clear
对第三张表进行操作
缺点:
? 因为第三张表是自动创建的,所以该表无法扩展和自定义字段,标的扩展性较差
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)
纯手写
通过模型表类,手动创建第三张关系表
通过手动建立第三张关系表,该表中的字段个数和字段名称都可以实现自定义
缺点:
? 自定义的第三张表,不能支持orm的跨表查询,也没有正反查询的概念
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)
半自动
在全自动的基础上,手动创建第三张表,并在MangyToManyField方法内再加一些参数(through=‘手动创建的第三张表名‘,through_fields=(‘外键字段1‘,‘外键字段2‘)
? ‘外键字段1‘当前所在表的外键
? ‘外键字段2‘关联表的外键
通过该方式创建第三张表,可以自定义添加任意字段,并且支持orm查询
缺点:
? 不支持 add
remove
set
clear
方法,对第三张表进行操作
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 Authors(models.Model): name = models.CharField(max_length=32) # 多对多关系字段 等价 books = models.ManyToManyField(to='Book', through='Book2Author', through_fields=("authors","book")) class Book2Author(models.Model): book = models.ForeignKey(to='Book') authors = models.ForeignKey(to='Authors') # 该表中可以由任意多的外键字段 # 可以扩展任意的字段
form组件
通过form组件,对用户输入的信息进行校验
如:
- 用户名规定不能含有敏感词
- 密码不能少于3位
def register(request): errors = {'username':'','password':''} if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') if '哈哈' in username: errors['username'] = '用户名包含敏感词' if len(password)<4: errors['password'] = '密码不能少于三个' return render(request,'register.html',locals())
<form action="" method="post"> <p>username: <input type="text" name="username"> <span style="color: red">{{ errors.username }}</span> </p> <p>password: <input type="text" name="password"> <span style="color: red">{{ errors.password }}</span> </p> <input type="submit"> <p></p> </form>
校验数据
使用form组件时,需要提前定义一个类
from django import forms class MyForm(forms.Form): username = forms.CharField(max_length=8,min_length=3) # 使用CharField字段,max_length表示最多位数,min_length表示最少位数 password = forms.IntegerField(max_value=8,min_value=3) # email字段 必须是邮箱格式 email = forms.EmailField()
通过 form_obj.is_valid()
方法,可以校验提交的数据是否符合条件
? 只有当数据全部符合校验规则的情况下,结果才是True
通过 form_obj.errors
方法,可以获取不符合规则的字段及错误的理由
通过 form_obj.cleaned
方法,可以获取校验通过的数据,返回的是一个字典
forms组件中定义的字段默认都是必须传值的,不能少传,如果少传会报错
forms组件只会校验forms类中定义的字段,如果多传了,不会有任何影响
渲染标签
<p>方式一:速度快,但是封装成程度太高,不能加标签样式</p> {{ form_obj.as_p }} {{ form_obj.as_ul }} {{ form_obj.as_table }}
<p>方式二:</p> #方式二写法过于繁琐 {{ form_obj.username.label }}{{ form_obj.username }} {{ form_obj.password.label }}{{ form_obj.password }} {{ form_obj.email.label }}{{ form_obj.email }}
<p>方式三:</p> #使用该方法,不管有多少个输入框,都可以通过for循环一次性渲染 {% for form in form_obj %} <p>{{ form.label }}{{ form }}</p>#和方式2中的对象点字段名一样 {% endfor %}
通过在自定义的MyForm
中的属性值设置label
的值,可以自定义前端label的是值
通过设置widget=forms.widgets.TextInput({‘class‘:‘form-control‘,键值对})
的值,可以对标签进行js渲染
展示信息
使用form组件,前端会自动识别,并帮你做校验
但是前端的校验保护措施过于薄弱,因此在写项目时,需要取消前端的校验
在后端进行真正的校验
让浏览器不做校验,需要在form表单中添加一个novalidate
参数
? <form action="" method="post" novalidate>
<form action="" method="post" novalidate> {% for form in form_obj %} <p>{{ form.label }}{{ form }} <span>{{ form.errors.0 }}</span></p> {% endfor %} <input type="submit"> </form>
通过{{ form.errors.0 }}
可以获取后端传过来的报错信息,报错信息为英文
在后端自定义的MyForm
类中,给属性值自定义error_messages
值,可是是前端得到自定义的报错信息
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':'jason'}) )
内置的校验器
通过validators
设置校验规则,规则一般为正则表达式
from django.core.validators import RegexValidator validators=[ RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头'), ]
勾子函数
符合所有字段规则的数据,才会进行勾子函数内的校验
form组件校验通过的数据会存放在cleaned_data中
局部钩子
# 校验用户名中不能含有666 局部钩子 def clean_username(self): username = self.cleaned_data.get('username') if '666' in username: # 给username所对应的框展示错误信息 # self.add_error('username','光喊666是不行的') raise ValidationError('到底对不对啊') # 将username数据返回 return username
全局钩子
```python
校验密码 确认密码是否一致 全局钩子
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
```
补充知识点
在自定义类的实行中设置 initial
值,会在前端中显示初始值
设置required
属性,默认为True,为True时该输入框不能为空,设置为False,可以为空
label input对应的提示信息 initial input框默认值 required 默认为True控制字段是否必填 widget 给input框设置样式及属性 widget=forms.widgets.PasswordInput({'class':'form-control c1 c2',}) widget=forms.widgets.TextInput({'class':'form-control c1 c2',}) widget=forms.widgets.TextInput(attrs={'class':'form-control c1 c2',})