Django的Form主要具有一下几大功能:
- 生成HTML标签
- 验证用户数据(显示错误信息)
- HTML Form提交保留上次提交数据
- 初始化页面显示内容
一 通过form实现校验字段功能
模型:models.py
class UserInfo(models.Model): name = models.CharField(max_length=32) pwd = models.CharField(max_length=32) email = models.EmaillField() tel = models.CharField(max_length=32)
模板:register.html
<form action="", method="post"> <p>用户名 <input type="text" name="username"></p> <p>密码<input type="password" name="password"></p> <p>确认密码<input type="password" name="repwd"></p> <p>邮箱<input type="text" name="email"></p> <p>手机号<input type="text" name="tel"></p> <input type="submit"> {% csrf_token %} </form>
二 通过form组件对模板进行渲染
form校验组件
from django import forms class UserForm(forms.Form): username = forms.CharField(min_length=4) password = forms.CharField(min_length=4) repwd = forms.CharField(min_length=4) email = forms.EmailField() tel = forms.CharField
视图函数
def register(req): if req.method == 'POST': # form = UserForm({'name':'cs', 'email': '123@qq.com', 'xxx':'harry'}) form = UserForm(req.POST) # 注意form表单的name属性值应该与forms组件字段名称一致 print(form.is_valid()) # 返回布尔值 if form.is_valid(): print(form.cleaned_data) # {"name":'cs", "email:'123@qq.com'} else: print(form.cleaned_data) print(form.errors) # {"name":["......"]}
return HttpResponse('ok')
''' if 所有的字段校验成功,则form.cleaned_data以一个字典的形式存放所有校验通过的数据 ''' return render(req, "register.html")
form组件在模板中渲染
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <form class="col-md-4 form-group" action="",method="post"> 2 <p> 3 用户名 {{ form.username }} 4 </p> 5 <p> 6 密码 {{ form.password }} 7 </p> 8 <p> 9 确认密码 {{ form.repwd }} 10 </p> 11 <p> 12 email {{ form.email }} 13 </p> 14 <p> 15 手机号 {{ form.tel }} 16 </p> 17 </form>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <form class="col-md-4 form-group" action="",method="post"> 2 {% csrf_token %} 3 {% for filed in form %} 4 <p> 5 <lable>{{ filed.label }}</lable> 6 {{ filed }} 7 </p> 8 {% endfor %} 9 </form>
展示错误信息
<form class="col-md-6" action="" method="post" novalidate> {% csrf_token %} {% for filed in form %} <p> <lable>{{ filed.label }}</lable> {{ filed }} <span>{{ filed.errors.0 }}</span> </p> {% endfor %} <input type="submit"> </form>
三 form组件的配置参数
Filed参数 required=Ture 是否必填 widget=None HTNL插件 label=None 用于生成Label标签显示内容 initial=None 初始值 help_text='' 帮助信息(在标签旁边显示) error_messages=None 错误信息{'required':'不能为空’, 'invalid':'格式错误’} show_hidden_initial=False 是否在当前插件后再加一个隐藏的具有默认值的插件(可用于两次输入是否一致) validators=[], 自定义验证规则 localize=False 是否支持本地化 disabled=False 是否可以编辑 label_suffix=None Lable内容后缀 CharField(Field) max_length=None 最大长度 min_length=None 最小长度 strip=True 是否移除用户输入空白 IntergerField(Field) max_value = None 最大值 min_value = None 最小值 DecimalField(IntergerField) max_value=None 最大值 min_value=None 最小值 max_digits=None 总长度 decimal_places=None 小数位长度 BaseTemporalField(Field) input_forats=None 时间格式化 DateField(BaseTemporalField) 格式:2015-09-01 TimeField(BaseTemporalField) 格式:11:12 DateTimeField(BaseTemporalField) 格式:2015-09-01 11:12 RegexField(charField) regex, 自定义正则表达式 max_length=None, 最大长度 min_length=None, 最小长度 error_message=None {"invalid":"..."} FileField(Field) allow_empty_file=False 是否允许空文件 ChoiceField(Field) choices=(), 选项,如:choices = ((0,'上海’),(1,,'北京') required=True 是否必填 widget=None 插件,默认select插件 label=None Label内容 initial=None 初始值 help_text='', 帮助提示 TypeChoiceField(ChoiceField) coerce = lambda val: val 对选中的值进行一次转换 empty_value = '' 空的默认值 ComboFiel(Field) fields=() 使用多个验证,如下:即验证最大程度20,又验证邮箱格式 fields.ComboField(fields=[fields.CharField(max_length=20),fields.EmailField(),] GenericIPAddressField protocol='both', both,ipv4,ipv6支持的IP格式 unpack_ipv4=False 解析ipv4地址,如果是::ffff:192.0.0.2.1 可以解析为192.0.0.2.1 SlugField(CharField) 数字,字母,下划线,减号(连接符)
在form中使用组件
from django.core.exceptions import NON_FIELD_ERRORS,ValidationError class UserForm(forms.Form): username = forms.CharField(min_length=2, label='用户名',
error_messages={"required": "该字段不能为空"},
widget= widgets.TextInput(attrs={'class':"form-control"})) password = forms.CharField(min_length=4,
label='密码',
widget=widgets.PasswordInput(attrs={'class':"form-control"})) repwd = forms.CharField(min_length=4,
label='重复密码',
widget= widgets.TextInput(attrs={'class':"form-control"})) email = forms.EmailField(label='邮箱',
error_messages={"required": "该字段不能为空", "invalid":"格式输入错误"}, widget= widgets.TextInput(attrs={'class': "form-control"})) tel = forms.CharField(label="电话号码",
error_messages={"required": "该字段不能为空"}, widget=widgets.TextInput(attrs={'class': "form-control"})) def clean_username(self): val = self.cleaned_data.get("username") # 获取输入的名字 ret = UserInfo.objects.filter(name=val) # 从数据库中查询是否有该用户存在if not ret: return val else: raise ValidationError("该用户已注册") def clean_tel(self): var = self.cleaned_data.get("tel") if len(var) == 11: return var else: raise ValidationError("手机号码必须为11位!") def clean(self): pwd = self.cleaned_data.get("password") r_pwd = self.cleaned_data.get("repwd") if pwd and r_pwd: if pwd == r_pwd: return self.cleaned_data else: raise ValidationError('两次密码不一致') else: return self.cleaned_data
在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的。
方式一:
1 from django.forms import Form 2 from django.forms import widgets 3 from django.forms import fields 4 from django.core.validators import RegexValidator 5 6 class MyForm(Form): 7 8 user = fields.ChoiceField( 9 # choices=((1, '上海'), (2, '北京'),), 10 initial=2, 11 widget=widgets.Select 12 ) 13 14 def __init__(self, *args, **kwargs): 15 super(MyForm,self).__init__(*args, **kwargs) 16 # self.fields['user'].widget.choices = ((1, '上海'), (2, '北京'),) 17 # 或 18 self.fields['user'].widget.choices = models.Classes.objects.all().value_list('id','caption')
方式二:
使用django提供的ModelChoiceField和ModelMultipleChoiceField字段来实现
1 from django import forms 2 from django.forms import fields 3 from django.forms import widgets 4 from django.forms import models as form_model 5 from django.core.exceptions import ValidationError 6 from django.core.validators import RegexValidator 7 8 class FInfo(forms.Form): 9 authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all()) 10 # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())
四 自定义验证规则
方式一 使用字段钩子或者全局钩子
from django.core.exceptions import NON_FIELD_ERRORS,ValidationError class UserForm(forms.Form): username = forms.CharField(min_length=2,
label='用户名',
error_messages={"required": "该字段不能为空"},
widget=widgets.TextInput(attrs={'class':"form-control"})) password = forms.CharField(min_length=4,
label='密码',
widget=widgets.PasswordInput(attrs={'class':"form-control"})) repwd = forms.CharField(min_length=4,
label='重复密码',
widget= widgets.TextInput(attrs={'class':"form-control"})) email = forms.EmailField(label='邮箱',
error_messages={"required": "该字段不能为空", "invalid":"格式输入错误"}, widget=widgets.TextInput(attrs={'class': "form-control"})) tel = forms.CharField(label="电话号码",
error_messages={"required": "该字段不能为空"}, widget=widgets.TextInput(attrs={'class': "form-control"}))
def clean_username(self): val = self.cleaned_data.get("username") # 获取输入的名字 ret = UserInfo.objects.filter(name=val) # 从数据库中查询是否有该用户存在if not ret: return val else: raise ValidationError("该用户已注册") def clean_tel(self): var = self.cleaned_data.get("tel") if len(var) == 11: return var else: raise ValidationError("手机号码必须为11位!") def clean(self): pwd = self.cleaned_data.get("password") r_pwd = self.cleaned_data.get("repwd") if pwd and r_pwd: if pwd == r_pwd: return self.cleaned_data else: raise ValidationError('两次密码不一致') else: return self.cleaned_data
方式二 使用
validators参数进行正则表达式匹配
1 from django.forms import Form 2 from django.forms import widgets 3 from django.forms import fields 4 from django.core.validators import RegexValidator 5 6 class MyForm(Form): 7 user = fields.CharField( 8 validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')], 9 )
通过正则表达式验证IP地址以及端口号
ipaddr_validate="^((?:(2[0-4]\d)|(25[0-5])|([01]?\d\d?))\.){3}(?:(2[0-4]\d)|(255[0-5])|([01]?\d\d?))$" port_validate='^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$'
from django.forms import Form,fields from django.forms import widgets,forms import re ipaddr_validate="^((?:(2[0-4]\d)|(25[0-5])|([01]?\d\d?))\.){3}(?:(2[0-4]\d)|(255[0-5])|([01]?\d\d?))$" port_validate='^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$' class dbinfo_create(Form): data_mode_type=fields.CharField(required=True,error_messages={'required':'数据库模型不能为空.'}) database_type=fields.CharField(required=True,error_messages={'required':'数据库类型不能为空'}) host=fields.RegexField(ipaddr_validate,required=True,error_messages={'required':'IP不能为空','invalid':'不合法的IP地址'}) port=fields.RegexField(port_validate,required=True,error_messages={'required':'端口不能为空.','invalid':'端口无效'}) # instance_nikename=fields.CharField(max_length=20,error_messages={'required':'端口不能为空.',"max_length":"标题不能超过20个字"}) db_business=fields.CharField(required=True,error_messages={'required':'请说明所属业务线'}) DBA=fields.CharField(required=True,error_messages={'required':'请说明DBA'}) responsible_person=fields.CharField(required=True, error_messages={'required':'请选择相关责任人!'})
五 通过Ajax提交并验证表单
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<form id="form"> <div class="form-group"> <p style="font-size: 26px; color: #337ab7">注册新用户</p> <p><span id="summary-error"></span></p> {% csrf_token %} <p> <span>用户名:</span>{{ form_obj.username }} <span class="pull-right"></span> </p> <p> <span>密码:</span>{{ form_obj.password }} <span class="pull-right"></span> </p> <p> <span>确认密码:</span>{{ form_obj.re_pwd }} <span class="pull-right"></span> </p> <div class="row"> <div class="col-md-10"> <p> Emil:{{ form_obj.email }} <span class="pull-right"></span> </p> </div> <p class="col-md-2"> <button class="pull-right btn-primary btn" style="margin-top: 18px" id="get_email_code">获取验证码</button> </p> </div> <p> <span>验证码</span>{{ form_obj.check_code }} <span class="pull-right"></span> </p> <label for="avatar"> <span>上传头像</span> <img id="avatar_img" width="80" height="80" src="/media/avatar/default.jpg" style="margin-left: 10px"> </label> <input type="file" id="avatar" style="display: none"> <div> <input type="button" id='submit_info' class="btn-primary btn pull-right" value="提交"> </div> </div> </form>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
function bindSubmit() { $('#submit_info').click(function () { $("#form p").removeClass('has-error'); $(".errors").html(''); var subData = new FormData(); var request_data = $('#form').serializeArray(); $.each(request_data, function (index, data) { subData.append(data.name, data.value); }); subData.append("avatar", $('#avatar')[0].files[0]); subData.append('csrfmiddlewaretoken', $("[name='csrfmiddlewaretoken']").val()); $.ajax({ url: "/register/", type: "post", processData: false, contentType: false, data: subData, dataType: 'json', success: function (data) { console.log(data.error_msg); if (!data.state) { if (data.summary_error) { $('#summary-error').html(data.summary_error) } $.each(data.error_msg, function (field, msg) { if (field == "__all__") { $('#summary-error').html(msg[0]).parent().addClass("has-error") } $('#id_' + field).next().html(msg[0]).addClass('errors').parent().addClass('has-error') }) } else { console.log(123); location.href = '/index/' } } }) }) }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class RegisterForm(forms.Form): ''' 注册form表单校验 ''' username = forms.CharField( max_length=32, error_messages={'required': '用户名不能为空'}, widget=widgets.TextInput(attrs={'class': "form-control"})) password = forms.CharField( max_length=32, error_messages={'required': '密码不能为空'}, widget=widgets.PasswordInput(attrs={'class': "form-control"})) re_pwd = forms.CharField( max_length=32, error_messages={'required': '密码不能为空'}, widget=widgets.PasswordInput(attrs={'class': "form-control"})) email = forms.EmailField( error_messages={'required': '邮箱不能为空'}, widget=widgets.EmailInput(attrs={'class': "form-control"})) check_code = forms.CharField( widget=widgets.TextInput(attrs={'class': "form-control"})) def clean_username(self): ''' 校验用户是否存在 :return: ''' username = self.cleaned_data.get('username') user_obj = models.UserInfo.objects.filter(username=username) if not user_obj: return username else: raise ValidationError('该用户已存在') def clean(self): ''' 校验两次输入的密码是否一致 :return: ''' password = self.cleaned_data.get('password') repwd = self.cleaned_data.get('re_pwd') if password == repwd: return self.cleaned_data else: raise ValidationError("两次密码输入不一致") def clean_email(self): ''' 校验注册邮箱是否已经注册 :return: ''' email = self.cleaned_data.get('email') user_obh = models.UserInfo.objects.filter(email=email) if not user_obh: return email else: raise ValidationError("该邮箱已被注册")
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def register(request): ''' 通过ajax实现用户注册 :param request: :return: ''' response = {'state': False, 'error_msg':"", 'summary_error': ""} if request.is_ajax(): form_obj = blog_forms.RegisterForm(request.POST) if form_obj.is_valid(): username = form_obj.cleaned_data.get('username') password = form_obj.cleaned_data.get('password') Email = form_obj.cleaned_data.get('email') valid_code = form_obj.cleaned_data.get('check_code') avatar_obj = request.FILES.get('avatar') check_valid = request.session['valid_code'] if valid_code == check_valid: extra = {} if avatar_obj: extra["avatar"] = avatar_obj blog_obj = models.Blog.objects.create( title="%s的博客" % username, site_name="%s的个人站点" % username, theme="default.css") models.UserInfo.objects.create_user(username=username, password=password, blog=blog_obj, email=Email, **extra) response['state'] = True return JsonResponse(response) else: response['summary_error'] = '验证码错误' return JsonResponse(response) else: response['error_msg'] = form_obj.errors return JsonResponse(response) elif request.method == 'GET': form_obj = blog_forms.RegisterForm() return render(request, 'register.html', {'form_obj': form_obj})