Django-form表单
Posted 日新其德止于至善
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django-form表单相关的知识,希望对你有一定的参考价值。
构建一个表单
假设你想构建一个登录页面,以获得用户名和密码,你需要类似的模板
<form action="" method="post"> <input type="text" name="username">用户名 <input type="password" name="pwd">密码 <input type="submit"> </form>
我们可能在表单提交之前,在浏览器端做一些验证。我们可能使用非常复杂的字段来完成验证(比如用户名不能为空,密码不含特殊字符等),这个时候,Django来为我们完成大部分工作是很容易的,它的优点在于:
(1)form表单 提交时,数据出现错误,返回的页面中仍可以保留之前输入的数
(2)可以方便的限制字段条件
在Django中构建一个表单
Form类
class UserForm(forms.Form): user = forms.CharField(max_length=10) pwd = forms.CharField(max_length=10)
自定义类UserForm继承了forms.Form类,该类带有两个字段,字段允许的最大长度通过max_length来定义。首先,它在html的<input>上放置一个max_length=‘10‘(这样浏览器将在第一时间阻止用户输入多于这个数目的字符),它还意味着当Django收到浏览器发送过来的表单时,它将验证数据的长度。
Form的实例具有一个is_valid方法,它为所有的字段运行验证的程序,当调用这个方法时,如果所有的字段都baohan 合法的数据,它将:返回True,并将表单的数据放到cleaned_data属性中。
views
from django.shortcuts import render,HttpResponse# Create your views here.def login(request): if request.method == ‘POST‘: print(request.POST) #< QueryDict: {‘pwd‘: [‘‘], ‘user‘: [‘‘]} > form = UserForm(request.POST) if form.is_valid(): print("cleaned_data",form.cleaned_data) print("errors",form.errors) return HttpResponse(‘OK‘) else: print("cleaned_data", form.cleaned_data) #cleaned_data {} print("errors", form.errors) #errors < ul class ="errorlist" > < li > pwd < ul class ="errorlist" > < li > This field is required.< / li > < / ul > < / li > < li > user < ul class ="errorlist" > < li > This fi print(‘form.errors["user"]‘,form.errors["user"]) #form.errors["user"] < ul class ="errorlist" > < li > This field is required.< / li > < / ul > print(‘form.errors["user"][0]‘,form.errors["user"][0]) #form.errors["user"][0] Thisfield is required. return render(request,‘login.html‘,{"form":form}) form = UserForm() return render(request,‘login.html‘,{"form":form})
发送给Django网站的表单数据通过一个视图处理,一般和发布这个表单的是同一个视图,这允许我们重用一些相同的逻辑,当处理表单时,我们需要在视图中实例化它。
如果访问视图的是一个GET请求,它将创建一个空的表单实例并将它放置到要渲染的模板的上下文中,这是我们在第一个访问该URL时预期发生的情况。
如果表单的提交使用POST请求,那视图将再次创建一个表单实例并使用请求中的数据来填充它:form=UserForm(request.POST),这叫做绑定数据至表单(它现在是一个绑定的表单)。
我们调用表单的is_valid()方法:如果它不为True,我们将带着这个表单返回到模板,这时表单不再为空,所以HTML表单将之前提交的数据填充,然后可以根据要求编辑并改正它。
如果is_valid()为True:我们将能够在cleaned_data属性中找到所有合法的表单数据,在发送HTTP重定向给浏览器告诉它下一步的去向之前,我们可以用这个数据来更新数据库或做其他处理。
Form表单类详解
未绑定的表单没有关联的数据,当渲染给用户时,它将为空或包含默认的值
绑定的表单具有提交的数据,因此可以用来检验数据是否合法。如果渲染一个不合法的绑定的表单,它将包含内联的错误信息,告诉用户如何纠正数据
#forms.py
from django import forms
class RegisterForm(forms.Form):
username = forms.CharField(max_length=100,
error_messages={"min_length":"最短为5个字符","required":"该字段不能为空"},
)
password = forms.CharField(max_length=100,
widget=widgets.PasswordInput(attrs={"placeholder":"password"})
)
telephone=forms.IntegerField(
error_messages={
"invalid":"格式错误"
}
)
gender=forms.CharField(
initial=2,
widget=widgets.Select(choices=((1,‘上海‘),(2,‘北京‘),))
)
email = forms.EmailField()
is_married = forms.BooleanField(required=False)
widgets:每个表单都有一个对应的Widget类,它对应一个HTML表单Widget,在大部分情况下,字段都有一个合理的默认Widget
字段的数据:不管表单提交的是什么数据,一旦通过is_valid()成功验证,验证后的表单数据位于form,cleaned_data字典中,这些数据已经为你转换好python的类型。此时,你依然可以从request.POST中直接访问到未验证的数据,但是访问验证后的数据更好一些。
在上面的表单示例中,is_married将是一个布尔值,类似的,IntergerField和FloatField字段分别将值转换为python的int和float。
Form组件的钩子
class UserForm(forms.Form): username = forms.CharField(min_length=4, max_length=10, error_messages={ "required":"用户名不能为空", "max_length":"用户名不能超过10位", "min_length":"用户名不能少于4位", }) password = forms.CharField(min_length=6, error_messages={ "required":"密码不能为空", "min_length":"密码不能少于6位" }) repeat_pwd = forms.CharField(error_messages={ "required":"确认密码不能为空" }) email = forms.EmailField(error_messages={ "invalid":"格式错误", "required":"邮箱不能为空" }) # 局部钩子 def clean_username(self): value = self.cleaned_data.get(‘username‘) if UserInfo.objects.filter(username=value).exists(): raise ValidationError(‘用户已存在‘) else: return value # 全局钩子 def clean(self): pwd = self.cleaned_data.get("password") repeat_pwd = self.cleaned_data.get("repeat_pwd") if pwd == repeat_pwd: return self.cleaned_data else: raise ValidationError("密码不一致")
<!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.min.css"> <title>reg</title> <style> body { background-color: #0378bb4a; } .form-horizontal { border: 20px solid #0378bb1a; padding-top: 30px; padding-bottom: 30px; padding-right: 15px; background-color: white; } .avator_img { width: 60px; height: 60px; margin-left: 20px; } #avatar { display: none; } span { font-size: 10px; margin-top: 5px; } </style> </head> <body> {% csrf_token %} <div class="container"> <div class="row"> <div class="col-md-5 col-md-offset-6" style="margin-top:50px"> <form class="form-horizontal"> <div style="padding-bottom: 10px;"><h4 class="text-center">注册账户</h4></div> {###############################用户名#########################################} <div class="form-group"> <div class="col-sm-offset-1 col-sm-10"> <input type="text" name=‘name12‘ class=" form-control" id="username" placeholder="用户名"> <span class="pull-right text-danger error"></span> </div> </div> {###############################密码#########################################} <div class="form-group"> <div class="col-sm-offset-1 col-sm-10"> <input type="password" name=‘pwd12‘ class="form-control" id="password" placeholder="密码"> <span class="pull-right text-danger error"></span> </div> </div> {###############################重复密码#########################################} <div class="form-group"> <div class="col-sm-offset-1 col-sm-10"> <input type="password" name=‘pwd‘ class="form-control" id="repeat_pwd" placeholder="重复密码"> <span class="pull-right text-danger error" ></span> </div> </div> {###############################邮箱#########################################} <div class="form-group"> <div class="col-sm-offset-1 col-sm-10"> <input type="email" name=‘email‘ class="form-control" id="email" placeholder="邮箱"> <span class="pull-right text-danger error"></span> </div> </div> {###############################头像#########################################} <div class="form-group"> <div class="col-sm-offset-1 col-sm-10"> <label for="avatar"> 上传头像<img src="/static/img/default.png" alt="" class="avator_img"></label> <input type="file" id="avatar" class="form-control"> </div> </div> {###############################注册#########################################} <div class="form-group"> <div class="col-sm-offset-1 col-sm-10"> <button type="button" class="btn btn-primary btn-block" id="reg_btn">注册</button> </div> </div> </form> </div> </div> </div> <script src="/static/jquery-3.3.1.min.js"></script> <script src="/static/bootstrap.min.js"></script> <script> {#头像预览#} $("#avatar").change(function () { var reader = new FileReader(); var choose_file = $(this)[0].files[0]; reader.readAsDataURL(choose_file); reader.onload = function () { $(".avator_img").attr("src", reader.result) }; }); {#注册#} $("#reg_btn").click(function () { var formdata = new FormData(); formdata.append("username", $("#username").val()); formdata.append("password", $("#password").val()); formdata.append("repeat_pwd", $("#repeat_pwd").val()); formdata.append("email", $("#email").val()); formdata.append("avatar", $("#avatar")[0].files[0]); formdata.append("csrfmiddlewaretoken",$("[name=‘csrfmiddlewaretoken‘]").val()); $.ajax({ url: ‘/reg/‘, type: "post", contentType: false, processData: false, data: formdata, success: function (data) { var data=JSON.parse(data); if (data.user) { location.href="/login/" } else { {#清空操作#} $("span.error").html(""); console.log(data.msg); var errors=data.msg; for ( var i in errors){ if (i==="__all__"){ $("#repeat_pwd").next().text(errors[i][0]) } else { $(‘#‘+i).next().text(errors[i][0]) } } } } }) }) </script> </body> </html>
template
手工渲染字段模板
<!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"> <title>login</title> </head> <body> <form action="" method="post" novalidate> {% csrf_token %} <p><input type="text" name="user">用户名 <span>{{ form.errors.user.0 }}</span></p> <p><input type="password" name="pwd">密码 <span>{{ form.errors.pwd.0 }}</span></p> <p><input type="submit"></p> </form> </body> </html>
由form表单提供的模板
<!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"> <title>login</title> </head> <body> {#第一种#} <form action="" method="post"> {% csrf_token %} {{ form.as_p }} <input type="submit"> </form> {#第二种#} <form action="" method="post"> {% csrf_token %} <div> 用户名{{ form.user }} </div> <div> 密码{{ form.pwd }} </div> <div> <input type="submit"> </div> </form> {#第三种#} <form action="" method="post"> {% csrf_token %} {% for field in form %} <div> <lable for="">{{ field.label }}</lable> {{ field }} </div> {% endfor %} <input type="submit"> </form> </body> </html>
对于<lable>/<input>对,还有几个输出选项:
{{ form.as_table }} {#以表格的形式将它们渲染在<tr>标签中#} {{ form.as_p }} {#将它们渲染在<p>标签中#} {{ form.as_ul }} {#将它们渲染在<li>标签中#} {#你必须自己提供<table>或<ul>元素#}
渲染表单的错误信息
{{ form.errors}}
{{ form.errors.user.0 }}
form组件补充
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类型
...
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
常用选择插件
# 单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
# )
以上是关于Django-form表单的主要内容,如果未能解决你的问题,请参考以下文章