Django框架(十)--ORM多对多关联关系三种创建方式、form组件

Django框架(十)--ORM多对多关联关系三种创建方式、form组件,第1张

Django框架(十)--ORM多对多关联关系三种创建方式、form组件

多对多的三种创建方式

1.全自动(就是平常我们创建表多对多关系的方式)

class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8,decimal_places=2)
authors = models.ManyToManyField(to='Author') class Author(models.Model):
name = models.CharField(max_length=32)
优势:不需要你手动创建第三张表
不足:由于第三张表不是你手动创建的,也就意味着第三张表字段是固定的无法做扩展

2.纯手动(手动建关系表)

class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8,decimal_places=2) class Author(models.Model):
name = models.CharField(max_length=32) class Book2Author(models.Model):
book = models.ForeignKey(to='Book')
author = models.ForeignKey(to='Author')
create_time = models.DateField(auto_now_add=True)
优势:第三张可以任意的扩展字段
不足:orm查询不方便

3.半自动(推荐使用)

class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8,decimal_places=2)
authors = models.ManyToManyField(to='Author',through='Book2Author',through_fields=('book','author'))
# through 告诉django orm 书籍表和作者表的多对多关系是通过Book2Author来记录的
# through_fields 告诉django orm记录关系时用过Book2Author表中的book字段和author字段来记录的
"""
多对多字段的
add
set
remove
clear不支持
""" class Author(models.Model):
name = models.CharField(max_length=32)
# books = models.ManyToManyField(to='Book', through='Book2Author', through_fields=('author', 'book')) #手动创建的第三张表
class Book2Author(models.Model):
book = models.ForeignKey(to='Book')
author = models.ForeignKey(to='Author')
create_time = models.DateField(auto_now_add=True)
优点:即不用自己创建第三张表了,也可以任意扩展字段了
缺点:这种多对多关系中不能使用add/set/remove/clear这些方法了

form组件


一、什么是form组件,可以干什么

1.forms组件就是一个类,可以检测前端传来的数据,是否合法。



例如,前端传来的邮箱数据,判断邮件格式对不对,用户名中不能以什么开头,等等 >>>校验数据
2.还可以前端页面搭建 >>> 渲染页面
3.展示错误信息 >>> 渲染错误信息


二、form组件的使用

1.使用语法

from django.shortcuts import render, HttpResponse
from django import forms #1.先写一个类,继承Form
class MyForm(forms.Form):
#定义属性,用来校验
#限制最大长度为8,最小长度为3
name = forms.CharField(max_length=8,min_length=3)
pwd = forms.CharField(max_length=8,min_length=3)
#校验邮箱
email = forms.EmailField() #2.在视图函数中使用MyForm来校验数据
#实例化产生对象,将需要校验的数据传入(可以传字典,可以不传),这个数据是前端传递过来的数据
myform = MyForm(request.POST) #3.校验,is_valid如果是true表示校验成功(满足myform里面的所有条件),反之,验证失败
if myform.is_valid():
print(myform.cleaned_data) #打印校验通过的数据
return HttpResponse('校验成功') else:
#校验失败的信息 是一个字典,它是所有错误信息 {错误字段名:[错误原因]}
print(myform.errors) #errors是一个列表,表示每个字段的错误信息
return HttpResponse('校验失败')

方法总结:

clean_data  验证通过的数据
errors 错误数据的对象
errors.as_data 错误数据的信息

注意事项

1.自定义的类中所有的字段默认都是必须要传值的
2.可以额外传入类中没有定义的字段名,forms组件不会去校验,也就是多传没有关系
3.实例化时,传入必须是字典,也可以不传
form_obj = views.LoginForm({'username':'jason','password':'','email':'[email protected]'})
form_obj.is_valid()
True form_obj = views.LoginForm({'username':'jason','password':''})
form_obj.is_valid()
False form_obj = views.LoginForm({'username':'jason','password':'','email':'[email protected]','hobby':'read'})
form_obj.is_valid()
True

2.组件的参数及其他 *** 作方式

max_length   #代表该字段最长可以为多少
min_length #代表该字段最短可以为多少
error_messages #设置错误信息的属性
required #默认值为True,意思是你前端传来的字段必须有它,没有的话校验失败
label #注释信息label ='用户名' 在前端渲染可以直接对象.label获取值
initial #设置默认值

widget #控制标签属性和样式
widget = widgets.PasswordInput() #你在模板渲染的时候,就会渲染成Input框,password样式
控制标签属性
widget = widgets.PasswordInput(attrs={'class':'form-control c1 c2','username':'jason'})

#例子
pwd = forms.CharField(max_length=8,min_length=3,required=True,label='密码',
error_messages = {'max_length':'最长是8','min_length':'最短是3','required':'这个必须填'}
)
其他 *** 作方式
# 单选的radio框
gender = forms.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框
hobby1 = 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
hobby2 = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=[1, 3],
widget=forms.widgets.CheckboxSelectMultiple()
)


三、渲染页面、渲染错误信息

#form组件可以在视图函数中使用,也可以在模板中使用
#视图层
def reg(request):
myform = MyForm()
   if request.method == 'POST':
   myform = MyForm(request.POST)
     print(request.POST) #<QueryDict: {'name': ['小张'], 'pwd': ['123123'], 'email': ['[email protected]']}> return render(request,'reg.html',locals()) #把myform对象传到前端页面了
#模板层
#第一种渲染方式(封装程度太高,一般只用于本地测试,通常不适用)
<form action='' method='post'>
{{ myform.as_p }}
{{ myform.as_ul }}
{{ myform.as_table}}
<input type='submit' value = '提交'></input>
</form> #第二种渲染页面的方式(可扩展性高,书写麻烦)
<form action='' method='post'>
{{myform.name.label}}:{{myform:name}}
{{myform.pwd.label}}:{{myform:pwd}}
{{myform.email.label}}:{{myform:email}}
<input type='submit' value = '提交'></input>
</form> #第三种渲染方式(推荐)
<form action='' method='post'>
{% for foo in myform %}
<p> {{ foo.lable }} : {{ foo }}
<span>{{ foo.errors.0 }}</span> //注意后面加0 错误信息
</p>
<input type='submit' value = '提交'></input>
</form>

注意事项:

1.forms组件在帮你渲染页面的时候 只会渲染获取用户输入的标签  提交按钮需要你手动添加
2.input框的label注释 不指定的情况下 默认用的类中字段的首字母大写

 

四、钩子函数(HOOK)

forms组件暴露给用户,可以自定义的校验规则    用法:在自定义的form类中定义一个函数即可

1.局部钩子(针对某一个字段做额外的校验) 

名字叫:clean_字段名,内部,取出该字段进行校验,如果通过,将该字段返回,如果失败,抛异常

#函数名:clean_字段名字
def clean_name(self): #self是当前form对象
name = self.cleaned_data.get('name') #从验证通过的数据中再次筛选
if '' in username:
#失败,抛异常,把异常信息以('key','value')形式写入add_error中
self.add_error('name','不能包含666')
#正常,把name返回到clean_data,将name写入clean_data字典中
return name

注意点:

  • 这些函数是写在自定义类form里面的,需要校验哪个字段就以 clean_字段名写一个校验函数
  • 校验失败,通过add_error('字段名','错误信息'),以{key:value}形式写入到errors字典中
  • 校验成功,返回name到clean_data,写入clean_data字典中

2.全局钩子(针对多个字段做额外的校验)

#重写clean方法
def clean(self):
#能走到这步,说明前面的校验已经通过了,下面校验两次两次密码是否输入一致
pwd = self.clean_data.get('pwd')
re_pwd = self.clean_data.get('re_pwd')
if pwd == re_pwd:
#校验成功,直接返回clean_data
return self.clean_data
else:
#校验失败,把错误信息放入errors中
self.add_error('re_pwd','两次密码输入不一致')
或者
#抛出异常
raise ValidationError('两次密码不一致')

注意点:

  • 校验失败,有两种写法,抛异常,将异常信息以{'__all__':[value,]}写入errors字典中
  • 校验成功,返回clean_data字典
  • 抛出异常类型为 ValidationError, from django.core.exceptions import ValidationError导入

钩子错误信息渲染注意点:

  • 局部钩子抛出的异常会添加到该字段中的错误信息中,获取错误信息

   前端: for循环对象  {{ foo.errors.0}}

  • 全局钩子抛出的异常会添加到__all__中,获取错误信息:

  后端: myform.errors.get('__all__')[0]   注意先判断myform.errors.get('__all__')是否存在

    前端:{{ myform.errors.__all__.0}}

  • 能走到这些函数,说明前面的form组件的那些校验都成功了,此时就可以从clean_data中取数据,因为此时clean_data中的数据全部符合,都找的到,而且clean_data是字典比较好取值。


 

五、完整的form组件校验

 1.视图层

from django.shortcuts import render, HttpResponse, redirect

# forms组件数据校验的功能
# 第一步:先要继承Form
from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError # 写一个类
class MyForm(forms.Form):
# 定义一个属性,可以用来校验字符串类型
# 限制最大长度是8,最小长度是3
name = forms.CharField(max_length=8, min_length=3, label='用户名'
error_messages={'max_length': '最长是8', 'min_length': '最短是3', 'required': '这个必须填'},
widget=widgets.TextInput(attrs={'class': 'form-control'})) pwd = forms.CharField(max_length=8, min_length=3, required=True, label='密码',
error_messages={'max_length': '最长是8', 'min_length': '最短是3', 'required': '这个必须填'},
widget=widgets.PasswordInput()) re_pwd = forms.CharField(max_length=8, min_length=3, required=True, label='确认密码',
error_messages={'max_length': '最长是8', 'min_length': '最短是3', 'required': '这个必须填'},
widget=widgets.PasswordInput())
# 校验是否是邮箱格式
email = forms.EmailField(label='邮箱', error_messages={'required': '这个必须填', 'invalid': '不符合邮箱格式'}) # aa = forms.CharField(label='选择', error_messages={'required': '这个必须填', 'invalid': '不符合邮箱格式'},widget=widgets.CheckboxInput())
def clean_name(self):
# self:当前form对象
name = self.cleaned_data.get('name')
if name.startswith('sb'):
# 失败,抛异常
raise ValidationError('不能以傻逼开头')
# 正常,把name返回
return name def clean(self):
pwd = self.cleaned_data.get('pwd')
re_pwd = self.cleaned_data.get('re_pwd')
if pwd == re_pwd:
return self.cleaned_data
else:
raise ValidationError('两次密码不一致') def index_form(request):
# 生成对象时(实例化),需要传入要校验的数据(字典)
if request.method == 'GET':
myform = MyForm()
return render(request,'indxe2.html',locals())
elif request.method == 'POST':
myform = MyForm(request.POST)
if myform.is_valid():
# print(myform.cleaned_data) # 验证通过的数据
# models.User.objects.create(name='lqz',pwd='123',re_pwd='123)
myform.cleaned_data.pop('re_pwd')
models.User.objects.create(**myform.cleaned_data)
return redirect('http://www.baidu.com')
else:
all_error = myform.errors.get('__all__')
if all_error:
all_error = all_error[0]
# print(myform.errors.as_data) return render(request, 'indxe3.html', locals())

2.模板层

<form action="" method="post" novalidate>
{% for foo in myform %}
<p>{{ foo.label }}:{{ foo }} <span>{{ foo.errors.0 }}</span></p>
{% endfor %} <input type="submit" value="提交"><span>{{ all_error }}</span>
</form>

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/588366.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-12
下一篇 2022-04-12

发表评论

登录后才能评论

评论列表(0条)

保存