Django Form
Form介绍
我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来。
与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否输入,输入的长度和格式等正不正确。如果用户输入的内容有错误就需要在页面上相应的位置显示对应的错误信息.。
Django form组件就实现了上面所述的功能。
总结一下,其实form组件的主要功能如下:
- 生成页面可用的HTML标签
- 对用户提交的数据进行校验
- 保留上次输入内容和渲染错误信息
pwd_err=my_form.errors.get('__all__')
from django import forms from django.forms import widgets from app01.models import UserInfo from django.core.exceptions import NON_FIELD_ERRORS, ValidationError class UserForm(forms.Form): name=forms.CharField(min_length=4,label="用户名",error_messages={"required":"该字段不能为空"}, widget=widgets.TextInput(attrs={"class":"form-control"}) ) pwd=forms.CharField(min_length=4,label="密码", widget=widgets.PasswordInput(attrs={"class":"form-control"}) ) r_pwd=forms.CharField(min_length=4,label="确认密码",error_messages={"required":"该字段不能为空"},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="手机号",widget=widgets.TextInput(attrs={"class":"form-control"})) def clean_name(self): val=self.cleaned_data.get("name") ret=UserInfo.objects.filter(name=val) if not ret: return val else: raise ValidationError("该用户已注册!") def clean_tel(self): val=self.cleaned_data.get("tel") if len(val)==11: return val else: raise ValidationError("手机号格式错误") def clean(self): pwd=self.cleaned_data.get('pwd') r_pwd=self.cleaned_data.get('r_pwd') if pwd and r_pwd: if pwd==r_pwd: return self.cleaned_data else: raise ValidationError('两次密码不一致') else: return self.cleaned_data
from django.shortcuts import render,HttpResponse # Create your views here. from app01.myforms import * def reg(request): if request.method=="POST": print(request.POST) #form=UserForm({"name":"yu","email":"123@qq.com","xxxx":"alex"}) form=UserForm(request.POST) # form表单的name属性值应该与forms组件字段名称一致 print(form.is_valid()) # 返回布尔值 if form.is_valid(): print(form.cleaned_data) # {"name":"yuan","email":"123@qq.com"} else: print(form.cleaned_data) # {"email":"123@qq.com"} # print(form.errors) # {"name":[".........."]} # print(type(form.errors)) # ErrorDict # print(form.errors.get("name")) # print(type(form.errors.get("name"))) # ErrorList # print(form.errors.get("name")[0]) # 全局钩子错误 #print("error",form.errors.get("__all__")[0]) errors=form.errors.get("__all__") return render(request,"reg.html",locals()) ''' form.is_valid() :返回布尔值 form.cleaned_data :{"name":"yuan","email":"123@qq.com"} form.errors :{"name":[".........."]} ''' form=UserForm() return render(request,"reg.html",locals())
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .error{ color: red; } </style> <!-- 最新版本的 Bootstrap 核心 CSS 文件 --> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <div class="container"> <div class="row"> <div class="col-md-6 col-lg-offset-3"> {#<h3>简单form</h3>#} {##} {##} {#<form action="" method="post" novalidate>#} {# {% csrf_token %}#} {# <p>用户名<input type="text" name="name"></p>#} {# <p>密码 <input type="text" name="pwd"></p>#} {# <p>确认密码 <input type="text" name="r_pwd"></p>#} {# <p>邮箱 <input type="text" name="email"></p>#} {# <p>手机号 <input type="text" name="tel"></p>#} {# <input type="submit">#} {##} {#</form>#} <hr> <h3>forms组件渲染方式1</h3> <form action="" method="post" novalidate> {% csrf_token %} <p>{{ form.name.label }} {{ form.name }} <span class="pull-right error">{{ form.name.errors.0 }}</span> </p> <p>{{ form.pwd.label }} {{ form.pwd }} <span class="pull-right error">{{ form.pwd.errors.0 }}</span> </p> <p>确认密码 {{ form.r_pwd }} <span class="pull-right error">{{ form.r_pwd.errors.0 }}</span><span class="pull-right error">{{ errors.0 }}</span> </p> <p>邮箱 {{ form.email }} <span class="pull-right error">{{ form.email.errors.0 }}</span></p> <p>手机号 {{ form.tel }} <span class="pull-right error">{{ form.tel.errors.0 }}</span></p> <input type="submit"> </form> {#<h3>forms组件渲染方式2</h3>#} {##} {#<form action="" method="post" novalidate>#} {# {% csrf_token %}#} {##} {# {% for field in form %}#} {##} {# <div>#} {# <label for="">{{ field.label }}</label>#} {# {{ field }}#} {# </div>#} {##} {# {% endfor %}#} {##} {# <input type="submit">#} {#</form>#} {##} {#<h3>forms组件渲染方式3</h3>#} {##} {#<form action="" method="post">#} {# {% csrf_token %}#} {##} {# {{ form.as_p }}#} {##} {# <input type="submit">#} {#</form>#} </div> </div> </div> </body> </html>
class RegForm(forms.Form): username = forms.CharField( min_length=6, max_length=32, label='用户名', initial='初始值', error_messages={ 'required':'必填', 'min_length':'最小长度', 'invalid':'' }, validators=[], widget=forms.TextInput(attrs={''}) ) gender = forms.ChoiceField(choices=[(1,'男'),(2,'女')]) def clean_username(self): pass # 校验成功 返回该字段的值 # 校验不成功 抛出异常 def clean(self): pass # 校验成功 返回所有的值 # 校验不成功 抛出异常 __all__ # self.add_error(filed,error) class RegForm(forms.ModelForm): password = forms.CharField(min_length=6, widget=forms.PasswordInput(attrs={'placeholder': '您的密码', 'autocomplete': 'off'})) re_password = forms.CharField(min_length=6, widget=forms.PasswordInput(attrs={'placeholder': '您的确认密码', 'autocomplete': 'off'})) class Meta: model = models.UserProfile fields = '__all__' # ['username'] exclude = ['is_active'] labels={ 'username':'用户名' } widgets = { 'username': forms.EmailInput(attrs={'placeholder': '您的用户名', 'autocomplete': 'off',}), # 'password':forms.PasswordInput(attrs={'placeholder':'您的密码','autocomplete':'off'}), 'mobile': forms.TextInput(attrs={'placeholder': '您的手机号', 'autocomplete': 'off'}), 'name': forms.TextInput(attrs={'placeholder': '您的真实姓名', 'autocomplete': 'off'}) } error_messages = { 'username': { 'required': '必填', 'invalid': '邮箱格式不正确' } } def clean_username(self): # 通过校验规则 返回当前字段的值 # 不通过校验规则 抛出异常 def clean(self): password = self.cleaned_data.get('password','') re_password = self.cleaned_data.get('re_password','') if password == re_password: # 对密码进行加密 md5 = hashlib.md5() md5.update(password.encode('utf-8')) self.cleaned_data['password'] = md5.hexdigest() return self.cleaned_data else: self.add_error('re_password', '两次密码不一致') raise ValidationError('两次密码不一致!!') def __init__(): #视图: form_obj = RegForm() render(request,'reg.html',{'form_obj':form_obj}) form_obj = RegForm(data=request.POST) form_obj.is_valid() form_obj.save() #新增: get form_obj = CustomerForm() post form_obj = CustomerForm(request.POST) form_obj.is_valid() form_obj.save() #编辑: get form_obj = CustomerForm(instance=obj) post form_obj = CustomerForm(request.POST,instance=obj) form_obj.is_valid() form_obj.save() #模板: {{ form_obj.as_p }} {{ form_obj.username }} ——》 # input 框 {{ form_obj.username.label }} ——》 # 中文提示 {{ form_obj.username.id_for_label }} ——》 # input 框的ID {{ form_obj.username.errors }} ——》 # 一个字段所有的错误 {{ form_obj.username.errors.0 }} ——》 # 一个字段第一个的错误 {{ form_obj.errors }} ——》 # 表单所有的错误 {{ form_obj.non_filed_errors }} ——》 # __all__的错误 {% for field in form_obj %} {{ field }} {{ field.label }} {{ field.id_for_label }} {{ field.errors.0 }} {% endfor %} {{ field.non_field_errors.0 }}
常用字段与插件
创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;
initial
初始值,input框里面的初始值。
class LoginForm(forms.Form): username = forms.CharField( min_length=8, label="用户名", initial="张三" # 设置默认值 ) pwd = forms.CharField(min_length=6, label="密码")
error_messages
重写错误信息。
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="密码")
password
class LoginForm(forms.Form): ... pwd = forms.CharField( min_length=6, label="密码", widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True) )
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.fields.ChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ), label="爱好", initial=3, widget=forms.widgets.Select() )
多选Select
class LoginForm(forms.Form): ... hobby = forms.fields.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ), label="爱好", initial=[1, 3], widget=forms.widgets.SelectMultiple() )
单选checkbox
class LoginForm(forms.Form): ... keep = forms.fields.ChoiceField( label="是否记住密码", initial="checked", widget=forms.widgets.CheckboxInput() )
多选checkbox
class LoginForm(forms.Form): ... hobby = forms.fields.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget=forms.widgets.CheckboxSelectMultiple() )
关于choice的注意事项:
在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的。
方式一:
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()) # 单选
Django Form所有内置字段
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类型
校验
方式一:
from django.forms import Form from django.forms import widgets from django.forms import fields from django.core.validators import RegexValidator class MyForm(Form): user = fields.CharField( validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')], )
方式二:
import re from django.forms import Form from django.forms import widgets from django.forms import fields from django.core.exceptions import ValidationError # 自定义验证规则 def mobile_validate(value): mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') if not mobile_re.match(value): raise ValidationError('手机号码格式错误') class PublishForm(Form): title = fields.CharField(max_length=20, min_length=5, error_messages={'required': '标题不能为空', 'min_length': '标题最少为5个字符', 'max_length': '标题最多为20个字符'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': '标题5-20个字符'})) # 使用自定义验证规则 phone = fields.CharField(validators=[mobile_validate, ], error_messages={'required': '手机不能为空'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'手机号码'})) email = fields.EmailField(required=False, error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))
补充进阶
应用Bootstrap样式
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css"> <title>login</title> </head> <body> <div class="container"> <div class="row"> <form action="/login2/" method="post" novalidate class="form-horizontal"> {% csrf_token %} <div class="form-group"> <label for="{{ form_obj.username.id_for_label }}" class="col-md-2 control-label">{{ form_obj.username.label }}</label> <div class="col-md-10"> {{ form_obj.username }} <span class="help-block">{{ form_obj.username.errors.0 }}</span> </div> </div> <div class="form-group"> <label for="{{ form_obj.pwd.id_for_label }}" class="col-md-2 control-label">{{ form_obj.pwd.label }}</label> <div class="col-md-10"> {{ form_obj.pwd }} <span class="help-block">{{ form_obj.pwd.errors.0 }}</span> </div> </div> <div class="form-group"> <label class="col-md-2 control-label">{{ form_obj.gender.label }}</label> <div class="col-md-10"> <div class="radio"> {% for radio in form_obj.gender %} <label for="{{ radio.id_for_label }}"> {{ radio.tag }}{{ radio.choice_label }} </label> {% endfor %} </div> </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <button type="submit" class="btn btn-default">注册</button> </div> </div> </form> </div> </div> <script src="/static/jquery-3.2.1.min.js"></script> <script src="/static/bootstrap/js/bootstrap.min.js"></script> </body> </html>
批量添加样式
可通过重写form类的init方法来实现。
class LoginForm(forms.Form): username = forms.CharField( min_length=8, label="用户名", initial="张三", error_messages={ "required": "不能为空", "invalid": "格式错误", "min_length": "用户名最短8位" } ... def __init__(self, *args, **kwargs): super(LoginForm, self).__init__(*args, **kwargs) for field in iter(self.fields): self.fields[field].widget.attrs.update({ 'class': 'form-control' })
ModelForm
form与model的终极结合。
class BookForm(forms.ModelForm): class Meta: model = models.Book fields = "__all__" labels = { "title": "书名", "price": "价格" } widgets = { "password": forms.widgets.PasswordInput(attrs={"class": "c1"}), }
class Meta:下常用参数:
model = models.Student # 对应的Model中的类 fields = "__all__" # 字段,如果是__all__,就是表示列出所有的字段 exclude = None # 排除的字段 labels = None # 提示信息 help_texts = None # 帮助提示信息 widgets = None # 自定义插件 error_messages = None # 自定义错误信息
Form组件
创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML
form组件的主要功能如下:
- 生成页面可用的HTML标签
- 对用户提交的数据进行校验
- 保留上次输入内容
from django import forms class LoginForm(forms.Form): def __init__(self, *args, **kwargs): super(LoginForm, self).__init__(*args, **kwargs) for field in iter(self.fields): self.fields[field].widget.attrs.update({ 'class': 'form-control' }) username = forms.CharField( min_length=8, label="用户名", initial="张三", error_messages={ "required": "不能为空", "invalid": "格式错误", "min_length": "用户名最短8位" } ) # password pwd = forms.CharField( min_length=6, label="密码", widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True) ) # 单radio值为字符串 gender = forms.fields.ChoiceField( required=False, choices=((1, "男"), (2, "女"), (3, "保密")), label="性别", initial=3, widget=forms.widgets.RadioSelect() ) # 单选Select hobby = forms.fields.ChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=3, widget=forms.widgets.Select() ) # 单选checkbox keep = forms.fields.ChoiceField( label="是否记住密码", initial="checked", widget=forms.widgets.CheckboxInput() ) # 多选checkbox hobby1 = forms.fields.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget=forms.widgets.CheckboxSelectMultiple() ) # 多选Select hobby2 = forms.fields.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget=forms.widgets.SelectMultiple() ) """ 关于choice的注意事项: 在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的。 """ # 方式一 from crm import models 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()) # 单选 # ModelForm """ model = models.Student # 对应的Model中的类 fields = "__all__" # 字段,如果是__all__,就是表示列出所有的字段 exclude = None # 排除的字段 labels = None # 提示信息 help_texts = None # 帮助提示信息 widgets = None # 自定义插件 error_messages = None # 自定义错误信息 """ from django.core.exceptions import ValidationError import hashlib from multiselectfield.forms.fields import MultiSelectFormField class BSModelForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for filed in self.fields.values(): if isinstance(filed, (MultiSelectFormField, forms.BooleanField)): continue if isinstance(filed.widget, forms.RadioSelect): continue filed.widget.attrs['class'] = 'form-control' class RegForm(forms.ModelForm): password = forms.CharField(min_length=6, widget=forms.PasswordInput(attrs={'placeholder': '您的密码', 'autocomplete': 'off'})) re_password = forms.CharField(min_length=6, widget=forms.PasswordInput(attrs={'placeholder': '您的确认密码', 'autocomplete': 'off'})) class Meta: model = models.UserProfile fields = '__all__' # ['username'] exclude = ['is_active'] labels = { 'username': '用户名' } widgets = { 'username': forms.EmailInput(attrs={'placeholder': '您的用户名', 'autocomplete': 'off', }), # 'password':forms.PasswordInput(attrs={'placeholder':'您的密码','autocomplete':'off'}), 'mobile': forms.TextInput(attrs={'placeholder': '您的手机号', 'autocomplete': 'off'}), 'name': forms.TextInput(attrs={'placeholder': '您的真实姓名', 'autocomplete': 'off'}) } error_messages = { 'username': { 'required': '必填', 'invalid': '邮箱格式不正确' } } def clean(self): password = self.cleaned_data.get('password', '') re_password = self.cleaned_data.get('re_password', '') if password == re_password: # 对密码进行加密 md5 = hashlib.md5() md5.update(password.encode('utf-8')) self.cleaned_data['password'] = md5.hexdigest() return self.cleaned_data else: self.add_error('re_password', '两次密码不一致') raise ValidationError('两次密码不一致!!')
一、注册实例
1、app01.models.py
from django.db import models class UserInfo(models.Model): nid = models.BigAutoField(primary_key=True) name = models.CharField(verbose_name='用户名', max_length=32, unique=True) pwd = models.CharField(verbose_name='密码', max_length=64) email = models.EmailField(verbose_name='邮箱', unique=True) tel = models.CharField(verbose_name='手机') avatar = models.ImageField(verbose_name='头像') create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
2、app01.myforms.py
from django import forms from django.forms import widgets from app01.models import UserInfo from django.core.exceptions import ValidationError class RegisterForm(forms.Form): name = forms.CharField( required=True, min_length=6, max_length=32, label="用户名", error_messages={ "required": "用户名不能为空", "min_length": "用户名长度不能小于6个字符", "max_length": "用户名长度不能大于32个字符"}, widget=widgets.TextInput(attrs={"class": "form-control"}) ) pwd = forms.CharField( required=True, min_length=6, max_length=32, label="密码", error_messages={ "required": "密码不能为空", "min_length": "密码长度不能小于6个字符", "max_length": "密码长度不能大于32个字符"}, widget=widgets.PasswordInput(attrs={"class": "form-control"}) ) r_pwd = forms.CharField( required=True, min_length=6, max_length=32, label="确认密码", error_messages={ "required": "密码不能为空", "min_length": "密码长度不能小于6个字符", "max_length": "密码长度不能大于32个字符"}, 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="手机号", widget=widgets.TextInput(attrs={"class": "form-control"}) ) # 局部钩子 def clean_name(self): val = self.cleaned_data.get("name") ret = UserInfo.objects.filter(name=val) if not ret: return val else: raise ValidationError("该用户已注册!") def clean_email(self): val = self.cleaned_data.get("email") ret = UserInfo.objects.filter(email=val) if not ret: return val else: raise ValidationError("该邮箱已注册!") def clean_tel(self): val = self.cleaned_data.get("tel") if len(val) == 11: return val else: raise ValidationError("手机号格式错误") # 全局钩子 def clean(self): pwd = self.cleaned_data.get('pwd') r_pwd = self.cleaned_data.get('r_pwd') if pwd and r_pwd: if pwd == r_pwd: return self.cleaned_data else: # raise ValidationError('两次密码不一致') # 加在全局的错误里面 form.errors.get("__all__") self.add_error("r_pwd", ValidationError('两次密码不一致')) # 加在r_pwd的错误里 else: return self.cleaned_data
3、app01.views.py
from django.shortcuts import render,HttpResponse from app01.myforms import * def reg(request): if request.method=="POST": form=RegisterForm(request.POST) # form表单的name属性值应该与forms组件字段名称一致 if form.is_valid(): # 验证成功做什么 form.cleaned_data.pop('r_pwd') user_info = form.cleaned_data UserInfo.objects.create(**user_info) return redirect('/') else: return render(request,"reg.html",locals()) form=RegisterForm() return render(request,"reg.html",locals()) ''' form=UserForm({"name":"bubu","email":"123@qq.com","xxxx":"123123123"}) :xxxx字段不会验证,只验证UserForm类里面有的字段 form.is_valid() :返回布尔值 form.cleaned_data :所有干净的字段以及对应的值{"name":"bubu","email":"123@qq.com"} form.errors :ErrorDict : {"校验错误的字段":["错误信息",]} form.errors.get("name") :ErrorList ["错误信息",] form.errors.get("name")[0] :取出第一个错误信息 全局钩子错误 forms中: raise ValidationError('两次密码不一致') #加在全局的错误里面 form.errors.get("__all__") self.add_error("r_pwd", ValidationError('两次密码不一致')) views.py中:errors=form.errors.get("__all__") 模板中:<span>{{ errors.0 }}</span> '''
- 内部原理 def login(request): if request.method == 'GET': return render(request,'login.html') else: obj = LoginForm(request.POST) # is_valid """ 1. LoginForm实例化时, self.fields={ 'user': 正则表达式 'pwd': 正则表达式 } 2. 循环self.fields flag = True errors cleaned_data for k,v in self.fields.items(): # 1. user,正则表达式 input_value = request.POST.get(k) 正则表达式和input_value flag = False return flag """ if obj.is_valid(): print(obj.cleaned_data) else: print(obj.errors) return render(request,'login.html')
def ajax_login(request): if request.method == 'GET': return render(request, 'login.html') else: import json ret = {'status': True,'msg': None} obj = LoginForm(request.POST) if obj.is_valid(): print(obj.cleaned_data) else: # print(obj.errors) # obj.errors对象 ret['status'] = False ret['msg'] = obj.errors v = json.dumps(ret) return HttpResponse(v) <body> <h1>用户登录</h1> <form id="f1" > {% csrf_token %} <p> <input type="text" name="user" /> </p> <p> <input type="password" name="pwd" /> </p> <a onclick="submitForm();">提交</a> </form> <script src="/static/jquery-1.12.4.js"></script> <script> function submitForm(){ $('.c1').remove(); $.ajax({ url: '/ajax_login/', type: 'POST', data: $('#f1').serialize(),// user=alex&pwd=456&csrftoen=dfdf\ dataType:"JSON", success:function(arg){ console.log(arg); if(arg.status){ }else{ $.each(arg.msg,function(index,value){ console.log(index,value); var tag = document.createElement('span'); tag.innerHTML = value[0]; tag.className = 'c1'; $('#f1').find('input[name="'+ index +'"]').after(tag); }) } } }) } </script> </body>
from django.shortcuts import render,redirect from app01 import models from django.forms import Form from django.forms import fields from django.forms import widgets class ClassForm(Form): title = fields.RegexField('全栈\d+') # def class_list(request): cls_list = models.Classes.objects.all() return render(request,'class_list.html',{'cls_list':cls_list}) def add_class(request): if request.method == "GET": obj = ClassForm() return render(request,'add_class.html',{'obj': obj}) else: obj = ClassForm(request.POST) if obj.is_valid(): # obj.cleaned_data # 字典 # 数据库创建一条数据 # print(obj.cleaned_data) # models.Classes.objects.create(title=obj.cleaned_data['tt']) models.Classes.objects.create(**obj.cleaned_data) return redirect('/class_list/') return render(request,'add_class.html',{'obj': obj}) def edit_class(request,nid): if request.method == "GET": row = models.Classes.objects.filter(id=nid).first() # 让页面显示初始值 # obj = ClassForm(data={'title': 'asdfasdfasdfas'}) # 有HTML标签,含有错误信息 obj = ClassForm(initial={'title': row.title}) # 只有HTML标签 return render(request,'edit_class.html',{'nid': nid,'obj':obj}) else: obj = ClassForm(request.POST) if obj.is_valid(): models.Classes.objects.filter(id=nid).update(**obj.cleaned_data) return redirect('/class_list/') return render(request,'edit_class.html',{'nid': nid,'obj':obj}) class StudentForm(Form): name = fields.CharField( min_length=2, max_length=6, widget=widgets.TextInput(attrs={'class': 'form-control'}) ) email = fields.EmailField(widget=widgets.TextInput(attrs={'class': 'form-control'})) age = fields.IntegerField(min_value=18,max_value=25,widget=widgets.TextInput(attrs={'class': 'form-control'})) cls_id = fields.IntegerField( # widget=widgets.Select(choices=[(1,'上海'),(2,'北京')]) widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),attrs={'class': 'form-control'}) ) def student_list(request): stu_list = models.Student.objects.all() return render(request,'student_list.html',{'stu_list':stu_list}) def add_student(request): if request.method == "GET": obj = StudentForm() return render(request,'add_student.html',{'obj':obj}) else: obj = StudentForm(request.POST) if obj.is_valid(): models.Student.objects.create(**obj.cleaned_data) return redirect('/student_list/') return render(request,'add_student.html',{'obj':obj}) def edit_student(request,nid): if request.method == "GET": row = models.Student.objects.filter(id=nid).values('name','email','age','cls_id').first() obj = StudentForm(initial=row) return render(request,'edit_student.html',{'nid':nid,'obj': obj}) else: obj = StudentForm(request.POST) if obj.is_valid(): models.Student.objects.filter(id=nid).update(**obj.cleaned_data) return redirect('/student_list/') return render(request,'edit_student.html',{'nid':nid,'obj': obj}) def teacher_list(request): tea_list = models.Teacher.objects.all() return render(request,'teacher_list.html',{'tea_list':tea_list}) from django.forms import models as form_model class TeacherForm(Form): tname = fields.CharField(min_length=2) # xx = form_model.ModelMultipleChoiceField(queryset=models.Classes.objects.all()) # xx = form_model.ModelChoiceField(queryset=models.Classes.objects.all()) xx = fields.MultipleChoiceField( # choices=models.Classes.objects.values_list('id','title'), widget=widgets.SelectMultiple ) def __init__(self,*args,**kwargs): super(TeacherForm,self).__init__(*args,**kwargs) self.fields['xx'].choices = models.Classes.objects.values_list('id','title') # obj = TeacherForm() # 1. 找到所有字段 # 2. self.fields = { # tname: fields.CharField(min_length=2) # } def add_teacher(request): if request.method == "GET": obj = TeacherForm() return render(request,'add_teacher.html',{'obj':obj}) else: obj = TeacherForm(request.POST) if obj.is_valid(): xx = obj.cleaned_data.pop('xx') row = models.Teacher.objects.create(**obj.cleaned_data) row.c2t.add(*xx) # [1,2] return redirect('/teacher_list/') return render(request,'add_teacher.html',{'obj':obj}) def edit_teacher(request,nid): if request.method == "GET": row = models.Teacher.objects.filter(id=nid).first() class_ids = row.c2t.values_list('id') # print(class_ids) # id_list = [] id_list = list(zip(*class_ids))[0] if list(zip(*class_ids)) else [] # obj = TeacherForm(initial={'tname':row.tname,'xx':[1,2,3]}) obj = TeacherForm(initial={'tname':row.tname,'xx':id_list}) return render(request,'edit_teacher.html',{'obj':obj}) # # class TestForm(Form): # t1 = fields.CharField( # widget=widgets.Textarea(attrs={}) # ) # # # t2 = fields.CharField( # widget=widgets.CheckboxInput # ) # # t3 = fields.MultipleChoiceField( # choices=[(1,'篮球'),(2,'足球'),(3,'溜溜球')], # widget=widgets.CheckboxSelectMultiple # ) # # t4 = fields.ChoiceField( # choices=[(1,'篮球'),(2,'足球'),(3,'溜溜球')], # widget=widgets.RadioSelect # ) # t5 = fields.FileField( # widget=widgets.FileInput # ) # def clean_t1(self): # pass from django.core.exceptions import ValidationError class TestForm(Form): user = fields.CharField(validators=[]) pwd = fields.CharField() def clean_user(self): v = self.cleaned_data['user'] if models.Student.objects.filter(name=v).count(): raise ValidationError('用户名已经存在') return self.cleaned_data['user'] def clean_pwd(self): return self.cleaned_data['pwd'] def clean(self): # user = self.cleaned_data.get('user') # email = self.cleaned_data.get('email') # if models.Student.objects.filter(user=user,email=email).count(): # raise ValidationError('用户名和邮箱联合已经存在') return self.cleaned_data # def _post_clean(self): # """ # An internal hook for performing additional cleaning after form cleaning # is complete. Used for model validation in model forms. # """ # pass def test(request): obj = TestForm(initial={'t3':[2,3]}) obj.is_valid() return render(request,'test.html',{'obj':obj})
<body> <h1>班级列表</h1> <div> <a href="/add_class/">添加</a> </div> <ul> {% for row in cls_list %} <li>{{ row.title }} <a href="/edit_class/{{ row.id }}/">编辑</a> </li> {% endfor %} </ul> </body> <body> <h1>添加班级</h1> <form method="POST" action="/add_class/" novalidate> {% csrf_token %} {{ obj.title }} {{ obj.errors.title.0 }} <input type="submit" value="提交" /> </form> </body> <body> <h1>编辑班级</h1> <form method="POST" action="/edit_class/{{ nid }}/"> {% csrf_token %} <p> {{ obj.title }} {{ obj.errors.title.0 }} </p> <input type='submit' value="提交" /> </form> </body> <body> <h1>添加学生</h1> <form action="/add_student/" method="POST"> {% csrf_token %} <p> {{ obj.name }} </p> <p> {{ obj.email }} </p> <p> {{ obj.age }} </p> <p> {{ obj.cls_id }} <!--/////////////////////////--> </p> <input type="submit" value="提交" /> </form> </body> <body> <h1>学生列表</h1> <a href="/add_student/">添加</a> <ul> {% for row in stu_list %} <li>{{ row.name }}-{{ row.email }}-{{ row.age }}-{{ row.cls_id }}-{{ row.cls.title }} <a href="/edit_student/{{ row.id }}/">编辑</a></li> {% endfor %} </ul> </body>
from django.forms import Form from django.forms import fields from django.forms import widgets from django.core.exceptions import ValidationError class RegisterForm(Form): username = fields.CharField( widget=widgets.TextInput(attrs={'class':'form-control'}) ) password = fields.CharField( widget=widgets.PasswordInput(attrs={'class':'form-control'}) ) password2 = fields.CharField( widget=widgets.PasswordInput(attrs={'class':'form-control'}) ) avatar = fields.FileField(widget=widgets.FileInput(attrs={'id':"imgSelect",'class':"f1" })) code = fields.CharField( widget=widgets.TextInput(attrs={'class':'form-control'}) ) def __init__(self,request,*args,**kwargs): super(RegisterForm,self).__init__(*args,**kwargs) self.request = request def clean_code(self): input_code = self.cleaned_data['code'] session_code = self.request.session.get('code') if input_code.upper() == session_code.upper(): return input_code raise ValidationError('验证码错误') # def clean_password2(self): # p1 = self.cleaned_data['password'] # p2 = self.cleaned_data['password2'] # return p2 def clean(self): p1 = self.cleaned_data.get('password') p2 = self.cleaned_data.get('password2') if p1 == p2: # return self.cleaned_data return None # self.add_error(None,ValidationError('密码不一致')) self.add_error("password2",ValidationError('密码不一致')) ################################################################### from app01.forms import RegisterForm from django.core.exceptions import NON_FIELD_ERRORS def register(request): """ 用户注册 :param request: :return: """ if request.method == "GET": obj = RegisterForm(request) return render(request,'register.html',{'obj':obj}) else: # 验证码操作 obj = RegisterForm(request,request.POST,request.FILES) if obj.is_valid(): pass else: print(obj.errors['__all__']) print(obj.errors[NON_FIELD_ERRORS]) # obj.errors """ { __all__: [错误1,错误2] user: [错误1,错误2] password: [错误1,错误2] } """ return render(request,'register.html',{'obj':obj}) ################################################################### # 方式一:views.py中:form.errors['__all__'] 或者 form.errors[NON_FIELD_ERRORS] 模板中:{{ form.non_field_errors }} 获取 __all__的错误 # 方式二:views.py中:errors=form.errors.get("__all__") 模板中:<span>{{ errors.0 }}</span>
4、templates.reg.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .error{ color: red; } .register { width: 400px; margin-top: 20px; margin-left: auto; margin-right: auto; border: 1px solid #f0f0f0; padding: 10px 30px 50px 30px; -webkit-box-shadow: 5px 10px 10px rgba(0, 0, 0, .05); box-shadow: 5px 10px 10px rgba(0, 0, 0, .05); } .register h3{font-size: 25px; text-align: center;font-weight: bold;} </style> <!-- 最新版本的 Bootstrap 核心 CSS 文件 --> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <div class="register"> <h3>用户注册</h3> <form action="" method="post" novalidate> {% csrf_token %} <div class="form-group"> <label for="id_name">{{ form.name.label }}</label> {{ form.name }} <span class="pull-right error">{{ form.name.errors.0 }}</span> </div> <div class="form-group"> <label for="id_pwd">{{ form.pwd.label }}</label> {{ form.pwd }} <span class="pull-right error">{{ form.pwd.errors.0 }}</span> </div> <div class="form-group"> <label for="id_r_pwd">确认密码</label> {{ form.r_pwd }} <span class="pull-right error">{{ form.r_pwd.errors.0 }}</span><span class="pull-right error">{{ errors.0 }}</span> </div> <div class="form-group"> <label for="id_email">邮箱</label> {{ form.email }} <span class="pull-right error">{{ form.email.errors.0 }}</span></div> <div class="form-group"> <label for="id_tel">手机号</label> {{ form.tel }} <span class="pull-right error">{{ form.tel.errors.0 }}</span></div> <input type="submit" class="btn btn-default" value="注册"/> </form> <!-- <h3>forms组件渲染方式2</h3> <form action="" method="post" novalidate> {% csrf_token %} {% for field in form %} <div class="form-group"> <label for="{{ field.auto_id }}">{{ field.label }}</label> {{ field }} <span class="pull-right error">{{ field.errors.0 }}</span> </div> {% endfor %} <input type="submit" class="btn btn-default" value="注册"/> </form> <h3>forms组件渲染方式3</h3> <form action="" method="post"> {% csrf_token %} {{ form.as_p }} <input type="submit" class="btn btn-default" value="注册"/> </form> --> </div> </body> </html>
二、常用字段与插件
from django import forms from django.forms import widgets from blog.models import UserInfo from django.core.exceptions import ValidationError from django.core.validators import RegexValidator class TestForm(forms.Form): user = forms.CharField( min_length=8, label="用户名", initial="张三", # 初始值,input框里面的初始值。 error_messages={ # 重写错误信息。 "required": "不能为空", "invalid": "格式错误", "min_length": "用户名最短8位" } ) pwd = forms.CharField( min_length=6, label="密码", widget=widgets.PasswordInput(attrs={'class': 'form-control'}) ) content = forms.CharField( widget=widgets.TextInput(attrs={'id': 'i1', 'class': 'form-control'}) ) # 单radio,值为字符串 gender = forms.ChoiceField( # choices=((1, '男'), (2, '女'),), initial=2, widget=widgets.RadioSelect() ) # 单checkbox keep = forms.fields.ChoiceField( label="是否记住密码", initial="checked", widget=forms.widgets.CheckboxInput() ) # 多选checkbox,值为列表 hobbies1 = forms.MultipleChoiceField( initial=[2, ], choices=((1, '足球'), (2, '篮球'),), widget=widgets.CheckboxSelectMultiple() ) # 单select,值为字符串 city = forms.ChoiceField( choices=((1, '上海'), (2, '北京'),), initial=2, widget=widgets.Select() ) # 多选select,值为列表 hobbies2 = forms.MultipleChoiceField( choices=((1, '足球'), (2, '篮球'),), initial=[1, ], widget=widgets.SelectMultiple() ) """ #choice字段注意事项 在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***获取的值无法实时更新***, 方法1:那么需要自定义构造方法从而达到此目的。 """ def __init__(self, *args, **kwargs): super(TestForm, self).__init__(*args, **kwargs) # self.fields['city2'].choices = ((1, '上海'), (2, '北京'),) # 或 self.fields['gender'].choices = ((1, '男'), (2, '女'),) self.fields['city'].choices = UserInfo.objects.all().value_list('id', 'caption') self.fields['hobbies1'].choices = UserInfo.objects.all().value_list('id', 'hobbies') self.fields['hobbies2'].choices = UserInfo.objects.all().value_list('id', 'hobbies') """ 在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***获取的值无法实时更新***, 方法2:使用django提供的ModelChoiceField和ModelMultipleChoiceField字段来实现 """ class TestForm2(forms.Form): authors = forms.ModelMultipleChoiceField(queryset=UserInfo.objects.all()) # authors = forms.ModelChoiceField(queryset=UserInfo.objects.all()) # authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all()) # 多选 # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all()) # 单选
1、表单的绑定属性 Form.is_bound:如果你需要区分绑定的表单和未绑定的表单,可以检查下表单的is_bound属性值 f = ContactForm({}) f.is_bound 空字典也是True 2、使用表单验证数据 Form.clean()如果你要自定义验证功能,那么你需要重新实现这个clean方法。 Form.is_valid()调用is_valid()方法来执行绑定表单的数据验证工作,并返回一个表示数据是否合法的布尔值。 Form.errors 表单的errors属性保存了错误信息字典 Form.errors.as_data() 返回一个字典,它将字段映射到原始的ValidationError实例。 Form.errors.as_json(escape_html=False) 返回JSON序列化后的错误信息字典。 Form.add_error(field, error) 向表单特定字段添加错误信息。 field参数为字段的名称。如果值为None,error将作为Form.non_field_errors()的一个非字段错误。 Form.has_error(field, code=None) 判断某个字段是否具有指定code的错误。当code为None时,如果字段有任何错误它都将返回True。 Form.non_field_errors() 返回Form.errors中不是与特定字段相关联的错误。 对于没有绑定数据的表单 验证没有绑定数据的表单是没有意义的。即 f = ContactForm({}) f.is_valid() 是False 3、检查表单数据是否被修改 Form.has_changed() 当你需要检查表单的数据是否从初始数据发生改变时,可以使用has_changed()方法。 Form.changed_data返回有变化的字段的列表。 4、访问表单中的字段 Form.cleaned_data:如果你的数据没有通过验证,cleaned_data字典中只包含合法的字段 通过fileds属性访问表单的字段: f.fields['name'].label = "Username" 5、表单的HTML生成方式 Form.as_p() Form.as_ul() Form.as_table()
Field required=True, 是否允许为空 widget=None, HTML插件 label=None, 用于生成Label标签或显示内容 initial=None, 初始值 help_text='', 帮助信息(在标签旁边显示) error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'} show_hidden_initial=False, 是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直) 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, 自定制正则表达式 phone = fields.RegexField('139\d+') 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类型 ...
TextInput(Input)
NumberInput(TextInput)
EmailInput(TextInput)
URLInput(TextInput)
PasswordInput(TextInput)
HiddenInput(TextInput)
Textarea(Widget)
DateInput(DateTimeBaseInput)
DateTimeInput(DateTimeBaseInput)
TimeInput(DateTimeBaseInput)
CheckboxInput
Select
NullBooleanSelect
SelectMultiple
RadioSelect
CheckboxSelectMultiple
FileInput
ClearableFileInput
MultipleHiddenInput
SplitDateTimeWidget
SplitHiddenDateTimeWidget
SelectDateWidget
""" 方式1:RegexValidator验证器 """ from django import forms from django.forms import widgets from django.core.exceptions import ValidationError from django.core.validators import RegexValidator from blog.models import UserInfo class FInfo(forms.Form): user = forms.CharField(max_length=5, validators=[RegexValidator(r'^[0-9]+$', '请输入数字', 'invalid'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')], ) def clean_user(self): val = self.cleaned_data.get("username") user = UserInfo.objects.filter(username=val).first() if not user: return val else: raise ValidationError("该用户已注册!") """ 方式2:自定义验证函数 """ import re from django import forms from django.forms import widgets from django.core.exceptions import ValidationError # 自定义验证规则 def mobile_validate(value): mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') if not mobile_re.match(value): raise ValidationError('手机号码格式错误') class PublishForm(forms.Form): title = forms.CharField(max_length=20, min_length=5, error_messages={'required': '标题不能为空', 'min_length': '标题最少为5个字符', 'max_length': '标题最多为20个字符'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': '标题5-20个字符'})) # 使用自定义验证规则--自定义方法 phone = forms.CharField(validators=[mobile_validate, ], error_messages={'required': '手机不能为空'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': '手机号码'})) email = forms.EmailField(required=False, error_messages={'required': '邮箱不能为空', 'invalid': '邮箱格式错误'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': '邮箱'}))
三、Hook方法(常见校验)
- 除了上面两种方式,我们还可以在Form类中定义钩子函数,来实现自定义的验证功能。
- 局部钩子:在Form类中定义 clean_字段名() 方法,就能够实现对特定字段进行校验。
- 全局钩子:在Form类中定义 clean() 方法,就能够实现对字段进行全局校验。
from django import forms from django.core.exceptions import ValidationError class LoginForm(forms.Form): username = forms.CharField( min_length=8, label="用户名", initial="张三", error_messages={ "required": "不能为空", "invalid": "格式错误", "min_length": "用户名最短8位" }, widget=forms.widgets.TextInput(attrs={"class": "form-control"}) ) password = forms.CharField( min_length=6, label="密码", widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}, render_value=True) ) re_password = forms.CharField( min_length=6, label="确认密码", widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}, render_value=True) ) # 定义局部钩子,用来校验username字段 def clean_username(self): value = self.cleaned_data.get("username") if "666" in value: raise ValidationError("光喊666是不行的") else: return value # 定义全局的钩子,用来校验密码和确认密码字段是否相同 def clean(self): password_value = self.cleaned_data.get('password') re_password_value = self.cleaned_data.get('re_password') if password_value == re_password_value: return self.cleaned_data else: self.add_error('re_password', '两次密码不一致') raise ValidationError('两次密码不一致') # 批量添加样式 def __init__(self, *args, **kwargs): super(LoginForm, self).__init__(*args, **kwargs) for field in iter(self.fields): self.fields[field].widget.attrs.update({ 'class': 'form-control' })
四、模板属性
属性 | 说明 |
---|---|
{{ field.label }} |
字段对应的label信息 |
{{ field.label_tag }} |
自动生成字段的label标签,注意与{{ field.label }} 的区别。 |
{{ field.id_for_label }} |
自定义字段标签的id |
{{ field.value }} |
当前字段的值,比如一个Email字段的值someone@example.com |
{{ field.html_name }} |
指定字段生成的input标签中name属性的值 |
{{ field.help_text }} |
字段的帮助信息 |
{{ field.errors }} |
包含错误信息的元素 |
{{ field.is_hidden }} |
用于判断当前字段是否为隐藏的字段,如果是,返回True |
{{ field.field }} |
返回字段的参数列表。例如{{ char_field.field.max_length }} |
Django提供了两种独立的方法,用于循环那些不可见的和可见的字段,hidden_fields()和visible_fields()
属性 |
---|
{% for field in form.visible_fields %}....{% endfor %} |
{% for field in form.hidden_fields %}....{% endfor %} |