Django-多对多建表与Form表单
一、多对多建表的三种创建方式:
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)
全自动型:
优点:至始至终都没有操作第三张表,全部都是由ORM自动创建的,还内置了四个操作第三张表的方法。(add,添加、remove,删除、set,设置、clear,清空)
缺点:全自动创建的表无法扩展修改字段,表的扩展性较差。
2.纯手撸型:(了解即可)
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)
纯手撸型:
优点:第三张表中的字段个数和字段名称全都可以自定义,
缺点:不再支持ORM跨表查询,不再有正反向的概念,没有,add,remove,set,clear方法。
3.半自动型:(推荐使用)
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) # 多对多关系字段 等价于上面的book表中的多对多关系字段 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')
半自动型:
优点:可以任意的添加和修改第三张表中的字段,并且支持ORM跨表查询
缺点:不支持add,remove,set,clear方法
二、Forms组件:
1.Forms组件介绍:
Form组件可以做的几件事情:
1.用户请求数据的验证;
2.自动生成错误信息;
3.打包用户提交的正确信息;
4.如果其中有一个错误了,其他的正确,保留上次输入的内容
5.自动创建input标签并可以设置样式
2.Form组件的使用:
(1).创建规则:
class Foo(Form): #必须继承 username = xxx password = xxx email = xxx 注意这里的字段必须和input的name字段一致
(2).数据和规则进行匹配先导入view.py
from django.forms import Form from django.forms import fields from django.forms import widgets
使用forms组件的第一步,必须先写一个类:
from django import forms from django.core.validators import RegexValidator from django.forms import widgets from django.core.exceptions import ValidationError class MyForm(forms.Form): # username字段 最少三位 最多八位,label设置input前面的字段,inital设置默认值 username = forms.CharField(max_length=8,min_length=3,label='用户名',initial='默认值', #通过error_messages,设置中文提示的错误信息 error_messages={ 'max_length':'用户名最长八位', 'min_length':'用户名最短三位', 'required':'用户名不能为空' },required=False,# False可以不进行Form验证,默认为True #通过widget=forms.widgets可以对标签属性设置, widget=forms.widgets.TextInput({'class':'form-control c1 c2','username':'jason'}) ) # password字段 最少三位 最多八位 password = forms.CharField(max_length=8,min_length=3,label='密码', error_messages={ 'max_length': '密码最长八位', 'min_length': '密码最短三位', 'required': '密码不能为空' },widget=forms.widgets.PasswordInput() ) 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':'邮箱格式错误' }) 当你觉得上面的所有的校验还不能满足你的需求,你可以考虑使用钩子函数,函数体内你可以写任意的校验代码 利用钩子函数进行限定用户输入: # 校验用户名中不能含有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 # 校验密码 确认密码是否一致 全局钩子 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 函数调用: 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())
数据的校验通常前后端都必须有的,但是前端的校验可有可无,并且弱不禁风,后端的校验必须有并且必须非常全面,
如何告诉浏览器不做校验,form表单中加一个novalidate参数即可。
#<form action="" method="post" novalidate>
index.html文件:
form action="" method="post" novalidate> {% for forms in form_obj %} <p> {{ forms.label }}{{ forms }} <span>{{ forms.errors.0 }}</span> </p> <!--<span>{{ forms.errors.0 }}获取错误信息添加input的后面--> {% endfor %} <input type="submit"> </form>
2.Form组件的其他方法使用:
radioSelect,单radio值为字符串 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() )
Form所有内置字段
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Field required=True, 是否允许为空 widget=None, HTML插件 label=None, 用于生成Label标签或显示内容 initial=None, 初始值 help_text='', 帮助信息(在标签旁边显示) error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'} validators=[], 自定义验证规则 localize=False, 是否支持本地化 disabled=False, 是否可以编辑 label_suffix=None Label内容后缀 CharField(Field) max_length=None, 最大长度 min_length=None, 最小长度 strip=True 是否移除用户输入空白 IntegerField(Field) max_value=None, 最大值 min_value=None, 最小值 FloatField(IntegerField) ... DecimalField(IntegerField) max_value=None, 最大值 min_value=None, 最小值 max_digits=None, 总长度 decimal_places=None, 小数位长度 BaseTemporalField(Field) input_formats=None 时间格式化 DateField(BaseTemporalField) 格式:2015-09-01 TimeField(BaseTemporalField) 格式:11:12 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12 DurationField(Field) 时间间隔:%d %H:%M:%S.%f ... RegexField(CharField) regex, 自定制正则表达式 max_length=None, 最大长度 min_length=None, 最小长度 error_message=None, 忽略,错误信息使用 error_messages={'invalid': '...'} EmailField(CharField) ... FileField(Field) allow_empty_file=False 是否允许空文件 ImageField(FileField) ... 注:需要PIL模块,pip3 install Pillow 以上两个字典使用时,需要注意两点: - form表单中 enctype="multipart/form-data" - view函数中 obj = MyForm(request.POST, request.FILES) URLField(Field) ... BooleanField(Field) ... NullBooleanField(BooleanField) ... ChoiceField(Field) ... choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),) required=True, 是否必填 widget=None, 插件,默认select插件 label=None, Label内容 initial=None, 初始值 help_text='', 帮助提示 ModelChoiceField(ChoiceField) ... django.forms.models.ModelChoiceField queryset, # 查询数据库中的数据 empty_label="---------", # 默认空显示内容 to_field_name=None, # HTML中value的值对应的字段 limit_choices_to=None # ModelForm中对queryset二次筛选 ModelMultipleChoiceField(ModelChoiceField) ... django.forms.models.ModelMultipleChoiceField TypedChoiceField(ChoiceField) coerce = lambda val: val 对选中的值进行一次转换 empty_value= '' 空值的默认值 MultipleChoiceField(ChoiceField) ... TypedMultipleChoiceField(MultipleChoiceField) coerce = lambda val: val 对选中的每一个值进行一次转换 empty_value= '' 空值的默认值 ComboField(Field) fields=() 使用多个验证,如下:即验证最大长度20,又验证邮箱格式 fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),]) MultiValueField(Field) PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用 SplitDateTimeField(MultiValueField) input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y'] input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'] FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中 path, 文件夹路径 match=None, 正则匹配 recursive=False, 递归下面的文件夹 allow_files=True, 允许文件 allow_folders=False, 允许文件夹 required=True, widget=None, label=None, initial=None, help_text='' GenericIPAddressField protocol='both', both,ipv4,ipv6支持的IP格式 unpack_ipv4=False 解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用 SlugField(CharField) 数字,字母,下划线,减号(连字符) ... UUIDField(CharField) uuid类型