Django Form表单
阅读目录
构建一个表单
导入模块
from django import forms from django.forms import widgets from django.core.exceptions import ValidationError
创建form类
from django import forms from django.forms import widgets from django.core.exceptions import ValidationError import re from django.contrib.auth.models import User class LoginForm(forms.Form): # 字段名字(username)就是渲染后input标签的name属性 username = forms.CharField(min_length=2, max_length=8, strip=True, # 是否移除用户输入空白 # error_messages 为错误触发的错误信息 error_messages={"required": "该字段不能为空", "min_length": "用户名长度不能小于2", "max_length": "用户名长度不能大于8"}, # 给input添加属性 widget=widgets.TextInput(attrs={ "class": "form-control", "placeholder": "2-8 位中文/字母/下划线", "id": "inputname"})) password = forms.CharField(min_length=6,max_length=20, strip=True, error_messages={"required":"该字段不能为空", "min_length":"密码长度不能小于6位", "max_length":"密码长度不能大于20位"}, widget = widgets.PasswordInput(attrs={ "class":"form-control", "placeholder":"密码需6-20个字符", "id":"inputPassword3"})) check_pwd = forms.CharField(min_length=6,max_length=20, strip=True, error_messages={"required":"该字段不能为空", "min_length":"密码长度不能小于6位", "max_length":"密码长度不能大于20位"}, widget = widgets.PasswordInput(attrs={ "class":"form-control", "placeholder":"请再次输入密码", "id":"inputPassword4"})) email = forms.EmailField(error_messages={'required': "邮箱不能为空", "invalid":"请输入有效的邮箱地址"}, widget = widgets.EmailInput(attrs={ "class":"form-control", "placeholder":"请输入邮箱", "id":"inputemail"}))
视图
from .forms import LoginForm def form_reg(request): if request.method == "POST": login_form = LoginForm(request.POST) # 将数据传给对应字段 绑定数据的表单实例 if login_form.is_valid(): # 判读是否全部通过验证 print("通过验证") print(login_form.cleaned_data) # 保存全部通过验证的表单数据 {'username': '周军豪123', 'password': '961023hao'} username = login_form.cleaned_data.get("username") password = login_form.cleaned_data.get("password") User.objects.create_user(username=username, password=password) print("数据库保存成功") return redirect("/log_in/") else: errors = login_form.errors # 字典类型,键是字段名,值是一个存着所有错误信息的列表 莫版中用{{ errors.字段名.0 }} # print(type(login_form.errors))# <class 'django.forms.utils.ErrorDict'> # login_form.errors={"user":["小于5位","不是数字"],"pwd":["",""]} error_all = errors.get("__all__") #全局钩子的错误信息保存在了键是 __all__ 的值的列表中,在模版语言中用{{ error_all.0 }} print(error_all) return render(request, "form_reg.html", {"errors": errors, "error_all":error_all, "login_form": login_form}) else:
login_form = LoginForm() # form组件的实例对象 未绑定表单实例 GET请求时渲染出input标签
return render(request, "form_reg.html", {"login_form": login_form})
模版
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <!-- 新 Bootstrap 核心 CSS 文件 --> <link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css"> <style> .container{ margin-top: 100px; } </style> </head> <body> <div class="container"> <div class="row"> <div class="col-md-6"> <form action="/login/" method="post"> {% csrf_token %} <div class="form-group"> <label for="user">user</label> {{ login_form.user }} <span>{{ errors.user.0 }}</span> </div> <div class="form-group"> <label for="pwd">pwd</label> {{ login_form.pwd }} <span>{{ errors.pwd.0 }}</span> </div> <div class="form-group"> <label for="gender">gender</label> {{ login_form.gender }}<span>{{ error_all.0 }}</span> </div> <p>{{ login_form.usersss }}</p> <button type="submit" class="btn btn-default">Submit</button> </form> <hr>
{# <form action="">#} {# {{ login_form.as_p }}#} {# </form>#} </div> </div> </div> </body> </html>
Django Form 类详解
绑定的和未绑定的表单实例
绑定的和未绑定的表单 之间的区别非常重要:
- 未绑定的表单没有关联的数据。当渲染给用户时,它将为空或包含默认的值。
- 绑定的表单具有提交的数据,因此可以用来检验数据是否合法。如果渲染一个不合法的绑定的表单,它将包含内联的错误信息,告诉用户如何纠正数据。
字段详解
Widgets
每个表单字段都有一个对应的Widget
类,它对应一个HTML 表单Widget
,例如<input type="text">
。
在大部分情况下,字段都具有一个合理的默认Widget。例如,默认情况下,CharField
具有一个TextInput Widget
,它在HTML 中生成一个<input type="text">
。
字段的数据
不管表单提交的是什么数据,一旦通过调用is_valid()
成功验证(is_valid()
返回True
),验证后的表单数据将位于form.cleaned_data
字典中。这些数据已经为你转换好为Python 的类型。
注:此时,你依然可以从request.POST
中直接访问到未验证的数据,但是访问验证后的数据更好一些。
在上面的联系表单示例中,is_married将是一个布尔值。类似地,IntegerField
和FloatField
字段分别将值转换为Python 的int
和float
。
表单字段详细参考:http://blog.csdn.net/qq_14898613/article/details/61617007
使用表单模版
表单渲染的选项
对于<label>/<input>
对,还有几个输出选项:
{{ form.as_table }}
以表格的形式将它们渲染在<tr>
标签中{{ form.as_p }}
将它们渲染在<p>
标签中{{ form.as_ul }}
将它们渲染在<li>
标签中
注意,你必须自己提供<table>
或<ul>
元素。
{{ form.as_p }}
会渲染如下:
<form action=""> <p> <label for="id_username">Username:</label> <input id="id_username" maxlength="100" name="username" type="text" required=""> </p> <p> <label for="id_password">Password:</label> <input id="id_password" maxlength="100" name="password" placeholder="password" type="password" required=""> </p> <p> <label for="id_telephone">Telephone:</label> <input id="id_telephone" name="telephone" type="number" required=""> </p> <p> <label for="id_email">Email:</label> <input id="id_email" name="email" type="email" required=""> </p> <p> <label for="id_is_married">Is married:</label> <input id="id_is_married" name="is_married" type="checkbox"> </p> <input type="submit" value="注册"> </form>
Form组件的钩子
局部钩子
def clean_username(self): # 函数名必须已clean_字段名的格式
user = self.cleaned_data.get("username")
if not User.objects.filter(username=user):
if not user.isdigit():
if re.findall(r"^[A-Za-z0-9_\-\u4e00-\u9fa5]+$",user):
return user # 通过检测,原数据返回 self.cleaned_data.get("username")
else:
raise ValidationError('用户名存在非法字符') # 没通过检测抛出错误,必须用ValidationError
else:
raise ValidationError("用户名不能为纯数字")
else:
raise ValidationError("该用户名已存在")
全局钩子
def clean(self): # 必须命名为clean
# 判断是否都通过检测,都不为None
if self.cleaned_data.get("password") and self.cleaned_data.get("check_pwd"):
if self.cleaned_data.get("password") == self.cleaned_data.get("check_pwd"):
return self.cleaned_data # 如果两次密码相同,返回干净的字典数据
else:
raise ValidationError("输入密码不一致") # 没通过检测返回异常信息
else:
return self.cleaned_data
form组件补充
1.Django内置字段
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, 自定制正则表达式 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类型 ...
2.Django内置插件
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
3.常用选择插件
# 单radio,值为字符串 # user = fields.CharField( # initial=2, # widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),)) # ) # 单radio,值为字符串 # user = fields.ChoiceField( # choices=((1, '上海'), (2, '北京'),), # initial=2, # widget=widgets.RadioSelect # ) # 单select,值为字符串 # user = fields.CharField( # initial=2, # widget=widgets.Select(choices=((1,'上海'),(2,'北京'),)) # ) # 单select,值为字符串 # user = fields.ChoiceField( # choices=((1, '上海'), (2, '北京'),), # initial=2, # widget=widgets.Select # ) # 多选select,值为列表 # user = fields.MultipleChoiceField( # choices=((1,'上海'),(2,'北京'),), # initial=[1,], # widget=widgets.SelectMultiple # ) # 单checkbox # user = fields.CharField( # widget=widgets.CheckboxInput() # ) # 多选checkbox,值为列表 # user = fields.MultipleChoiceField( # initial=[2, ], # choices=((1, '上海'), (2, '北京'),), # widget=widgets.CheckboxSelectMultiple # )
既然选择了远方,便是风雨兼程...