目录css
class Book(models.Model): title = models.CharField(max_length=32) # # 经过ORM自带的ManyToManyField自动建立第三张表 authors=models.ManyToManyField(to='Author') class Author(models.Model): name = models.CharField(max_length=32)
优势:操做简便,第三张表都是orm自动建立html
内置的操做第三张表的方法:add、remove、set、clear前端
缺点:自动建立的第三张表没法扩展、修改字段,表的扩展性较差python
class Book(models.Model): title = models.CharField(max_length=32) class Author(models.Model): name = models.CharField(max_length=32) # 本身建立第三张表,分别经过外键关联书和做者 class BookAuthor(models.Model): book = models.ForeignKey(to='Book') author = models.ForeignKey(to = 'Author') create_time = models.DataField(auto_now_add = True)
优势:第三张表的字段个数和类型均可以本身定义,灵活性更高;jquery
缺点:再也不支持orm跨表查询,就没有了正反向的概念;git
当ManyToManyField只有一个参数to的状况下, orm会自动帮你建立第三张表;
若是还有额外的through和through_fields参数,第三张表就要本身建立, orm还会维护与第三张表的关系维,可以继续使用orm的跨表查询;
through 指定第三张关系表;
through_fields 指定第三张关系表中的两个外键字段。ajax
class Book(models.Model): title = models.CharField(max_length=32) author = models.ManyToManyField(to='Author',,through='BookAuthor',through_fields=('book','author')) class Author(models.Model): name = models.CharField(max_length=32) book = models.ManyToManyField(to='Book',,through='BookAuthor',through_fields=('author','book')) # through_fields接受一个元组('field1','field2'):其中field1是定义ManyToManyField的模型外键的名(author),field2是关联目标模型(book)的外键名。 class BookAuthor(models.Model): book = models.ForeignKey(to='Book') author = models.ForeignKey(to = 'Author') create_time = models.DataField(auto_now_add = True)
优势:能够任意的添加和修改第三张表中的字段,而且支持orm跨表查询正则表达式
缺点:再也不支持add、remove、clear、set方法的使用数据库
form表单的主要功能:django
生成页面可用的HTML标签;对用户提交的数据进行校验;保留上次的输入内容;
需求:在浏览器页面,经过form表单中的input框获取用户输入,传到后端进行校验用户的输入是否合法;而后在对应的input框后展现一些提示信息;
这里利用标签的渲染(其实用ajax和BOM操做也能实现)
def register(request): back_dic = {'username':'','password':''} if request.method == 'POST': username = request.POST.get('usename') password = request.POST.get('password') if '小朋友' in username: back_dic['usernmae'] = '敏感信息!不符合!' if len(password) < 4: back_dic['password'] = '你过短!不行呀!' return render(request,'register.html',local())
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link rel="stylesheet" href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css"> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> </head> <body> </body> </html>
以上须要本身手写HTML代码获取用户输入,传到后端,若是数据有误,还需展现错误信息,比较麻烦,下面利用django自带的form组件来实现。
使用form组件首先要定义一个类,继承django的Form类
# views.py from django import forms class MyForm(forms.Form): # username字段,最少3位,最多8位 username = forms.CharField(max_length=8,min_length=3) # password字段,最少3位,最多8位 password = forms.CharField(max_length=8,min_length=3) # email字段,必须是邮箱格式 email = forms.EmailField()
# 在Django Console中校验 自动搭建测试环境 In[2]:from app01 import views In[3]:form_obj = views.MyForm({'username':'zhang','password':'123','email':'110117@qq.com'}) In[4]:form_obj.is_valid Out[4]: <bound method BaseForm.is_valid of <MyForm bound=True, valid=Unknown, fields=(username;password;email)>> In[5]:form_obj.is_valid() Out[5]: True # 只有当你传入的所有数据都符合校验规则的状况下才为True,不然为False # 查看不符合规则的字段和错误理由 In[6]:form_obj.errors Out[6]: {} # 符合规则的数据,不符合的会被排除掉 In[7]:form_obj.cleaned_data Out[7]: {'username': 'zhang', 'password': '123', 'email': '110117@qq.com'} # 若是出入的数据不符合定义字段指定的长度,错误的字段会放在form_obj.errors中;好比: In[8]:form_obj.errors Out[8]:{'password': ['Ensure this value has at least 3 characters (it has 2).'],'email': ['Enter a valid email address.'] } # 传数据的时候,多传不报错 In[9]:form_obj = views.MyForm({'username':'zhang','password':'123','email':'110117@qq.com','gender':'male'}) In[10]form_obj.is_valid() Out[10]: True # 少传对应的字段就不行 In[11]:form_obj = views.MyForm({'username':'zhang','password':'123'}) In[12]:form_obj.is_valid() Out[13]: False
forms组件只会帮你渲染获取用户输入的标签 不会帮你渲染提交按钮 须要你本身手动添加
字段类括号内的label能够修改input框前面的注释(提示信息)
from django import forms class MyForm(forms.Form): username = forms.CharField(max_length=8,min_length=3,label='用户名') # label 修改用户输入框前面的提示信息 password = forms.CharField(max_length=8,min_length=3,label='密码') email = forms.EmailField(label='邮箱') def index(request): form_obj = MyForm(request.POST) return render(request, 'index.html', locals())
forms组件渲染标签方式1
封装程度态高 不推荐使用 可是能够用在本地测试
<p>forms组件渲染标签方式1</p> {{ form_obj.as_p }} <!--自动渲染全部input框 --> {{ form_obj.as_ul }} {{ form_obj.as_table }}
forms组件渲染标签方式2
不推荐使用 写起来太烦了
<p>forms组件渲染标签方式2</p> <!--获取input框前面的Username 获取的是一个input输入框--> {{ form_obj.username.label }}{{ form_obj.username }} {{ form_obj.username.label }}{{ form_obj.password }} {{ form_obj.username.label }}{{ form_obj.email }}
forms组件渲染标签方式3
推荐使用
<p>forms组件渲染标签方式3:推荐使用 </p> {% for form in form_obj %} <p>{{ form.label }}{{ form }}</p> <!--form 等价于你方式2中的对象点字段名,是一个input输入框--> {% endfor %}
from django import forms class MyForm(forms.Form): username = forms.CharField(max_length=8,min_length=3,label='用户名', error_messages={ 'max_length':'用户名不能超过八位', 'min_length':'用户名不能少于三位', 'required':'用户名不能为空', } ) # label 修改用户输入框前面的提示信息 password = forms.CharField(max_length=8,min_length=3,label='密码', error_messages={ 'max_length': '密码不能超过八位', 'min_length':'密码不能少于三位', 'required': '密码不能为空', } ) email = forms.EmailField(label='邮箱', error_messages={ 'required':'邮箱不能为空!', 'invalid':'邮箱格式错误', }) def index(request): form_obj = MyForm() print(request.POST) # <QueryDict: {'username': ['zhang'], 'password': ['123456'], 'email': ['1101172195@qq.com']}> if request.method=='POST': form_obj = MyForm(request.POST) # MyForm类须要传入一个字典,而request.POST获取的用户的输入偏偏也是字典 if form_obj.is_valid(): # 跟上面的变量名要一致,由于两次不冲突,都要拿到html文件中给同一份代码渲染 print(form_obj.cleaned_data) return HttpResponse('输入真确!') else: print(form_obj.errors) return render(request, 'index.html', locals())
novalidate 在form标签内写上它,告诉前端不让其对用户输入的数据作校验
forms.errors 获取后端校验后错误的报错信息,传给前端;
forms.errors.0 改变报错信息在input输入框后面展现
forms.label input框前面的提示信息
forms 获取一个input输入框
{#novalidate让前端不对输入信息是否合法作校验#} <form action="" method="post" novalidate> <p>form组件的渲染方式3:</p> <!--推荐使用--> {% for forms in form_obj %} <p> {# 获取提示信息和input输入框#} {{ forms.label }}{{ forms }} {# 这里forms.errors也能拿到后端的报错信息(在input框下面),渲染到浏览器,forms.errors.0把报错信息的展现到input框后面#} <span>{{ forms.errors.0 }}</span> </p> {% endfor %} <input type="submit"> </form>
字段类括号内能够连续写多个参数
from django.core.validators import RegexValidator class MyForm(Form): user = fields.CharField( validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')], )
当你以为上面的全部的校验还不可以知足你的需求 ,你能够考虑使用钩子函数,是一个函数 函数体内你能够写任意的校验代码。
钩子函数执行条件:在类内部定义的字段括号设定的条件都知足以后才会走到钩子函数;若是前面的条件都不知足,不会走到钩子函数。
用法:直接在类内部定义django给咱们定义好的函数,根据须要本身选择。包括:
clean、clean_confirm_password、clean_email、clean_password、clean_username
局部钩子:校验单个字段
全局钩子:校验多个字段
# 使用forms组件的第一步 必须先写一个类 from django import forms class MyForm(forms.Form): # username字段 最少三位 最多八位 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'}) ) # 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
其余字段及参数
label input对应的提示信息 initial input框默认值 required 默认为True控制字段是否必填 widget 给input框设置样式及属性,也能设置input输入框的类型(type) widget=forms.widgets.TextInput({'class':'form-control c1 c2','username':'li'}) # 增长属性 widget=forms.widgets.TextInput(attrs={'class':'form-control c1c2','username':'li'}) widget=forms.widgets.TextInput() # 默认普通文本类型 widget=forms.widgets.PasswordInput() # 密文密码
radioSelect
单radio值为字符串
class LoginForm(forms.Form): username = forms.CharField( min_length=8, label="用户名", initial="张三", error_messages={ "required": "不能为空", "invalid": "格式错误", "min_length": "用户名最短8位" } ) pwd = forms.CharField(min_length=6, label="密码") gender = forms.fields.ChoiceField( choices=((1, "男"), (2, "女"), (3, "保密")), label="性别", initial=3, widget=forms.widgets.RadioSelect() )
单选Select
class LoginForm(forms.Form): ... hobby = forms.ChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ), label="爱好", initial=3, widget=forms.widgets.Select() )
多选Select
class LoginForm(forms.Form): ... hobby = forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ), label="爱好", initial=[1, 3], widget=forms.widgets.SelectMultiple() )
单选checkbox
class LoginForm(forms.Form): ... keep = forms.ChoiceField( label="是否记住密码", initial="checked", widget=forms.widgets.CheckboxInput() )
多选checkbox
class LoginForm(forms.Form): ... hobby = forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget=forms.widgets.CheckboxSelectMultiple() )
choice字段注意事项
在使用选择标签时,须要注意choices的选项能够配置从数据库中获取,可是因为是静态字段 获取的值没法实时更新,须要重写构造方法从而实现choice实时更新。
方式一:
from django.forms import Form from django.forms import widgets from django.forms import fields class MyForm(Form): user = fields.ChoiceField( # choices=((1, '上海'), (2, '北京'),), initial=2, widget=widgets.Select ) def __init__(self, *args, **kwargs): super(MyForm,self).__init__(*args, **kwargs) # self.fields['user'].choices = ((1, '上海'), (2, '北京'),) # 或 self.fields['user'].choices=models.Classes.objects.all().values_list('id','caption')
方式二:
from django import forms from django.forms import fields from django.forms import models as form_model class FInfo(forms.Form): authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all()) # 多选 # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all()) # 单选
Field required=True, 默认为空,True不能为空,False容许为空 widget=None, HTML插件 label=None, 用于修改input框前面的提示信息 initial=None, input框初始值 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类型 Django Form内置字段