Python自动化开发学习22-Django下(Form)
Form表单验证
这里不是验证用户名密码是否正确,这部分内容之前已经讲过了。这里要验证的是数据格式,这步验证是在收到请求后先执行的验证。只有数据格式验证通过,才会验证用户名密码是否正确。如果数据格式验证不通过,则返回错误信息。
讲师的博客地址:http://www.cnblogs.com/wupeiqi/articles/6144178.html
测试环境
先写一个form表单,host.html:
<form action="/host/" method="POST">{% csrf_token %}<p><input type="text" name="hostname" placeholder="主机名"></p><p><input type="text" name="ip" placeholder="IP地址"></p><p><input type="text" name="port" placeholder="端口号"></p><p><input type="text" name="email" placeholder="E-Mail"></p><p><input type="submit" value="登录"></p>
</form>
然后导入验证的模块写一个类:
from django import forms
class FM(forms.Form):# 变量的字段名匹配form表单里的name属性的值,必须一样hostname = forms.CharField()ip = forms.GenericIPAddressField(protocol='ipv4')port = forms.IntegerField()email = forms.EmailField()# 上面只有这么几个,如果提交的数据有很多,那么其他数据都不收(丢弃)
然后是在处理函数里,通过这个类进行验证:
def host(request):if request.method == 'GET':return render(request, 'host.html')if request.method == 'POST':obj = FM(request.POST) # 实例化,把POST的数据传入res = obj.is_valid() # 获取结果print(res) # 验证通过是True,不通过则是Falseif res:print(obj.cleaned_data) # 这是一个原生的字典,里面就是提交来的数据return HttpResponse('OK')else:print(obj.errors) # 这里是一串html的列表的代码print(type(str(obj.errors))) # 这里的str方法居然是django提供的,变成(<class 'django.utils.safestring.SafeText'>)print(obj.errors.as_json()) # 也可以拿到JSON的格式return HttpResponse(str(obj.errors)) # 通过str方法后,页面上会直接按html代码处理
现在可以打开页面测试效果。
验证通过,obj.cleaned_data 里就是合法的数据的字典,可以进行后续的操作。
验证不通过,obj.errors是错误信息(html的格式,一个ul无序列表),也可以通过 obj.errors.as_json() 获取一个JSON格式的错误信息:
{"hostname": [{"message": "This field is required.", "code": "required"}],"ip": [{"message": "Enter a valid IPv4 address.", "code": "invalid"}],"port": [{"message": "Enter a whole number.", "code": "invalid"}],"email": [{"message": "Enter a valid email address.", "code": "invalid"}]}
错误信息包括,错误类型(code)和错误信息(message),这里的错误信息也可以自定义,我要中文的。
自定义错误信息
通过参数error_messages设置自定义的错误信息,code的值就是key,然后把你希望的内容填在value里:
from django import forms
class FM(forms.Form):# 变量的字段名匹配form表单里的name属性的值,必须一样hostname = forms.CharField(max_length=12,min_length=6,error_messages={'required': "设备名不能为空",'max_length': "设备名太长,不能超过12",'min_length': "设备名太短,不能小于6"})ip = forms.GenericIPAddressField(protocol='ipv4')port = forms.IntegerField()email = forms.EmailField(error_messages={'required': "邮箱不能为空", 'invalid': "邮箱格式错误"})
返回错误信息到表单页面
现在使用render方法,把错误信息返回给页面。所有内容都在obj里,把整个obj返回:
return render(request, 'host.html', {'obj': obj})
通过模板语言获取错误信息。这里注意最后要在.0拿到的才是错误信息的内容
<form action="/host/" method="POST">{% csrf_token %}<p><input type="text" name="hostname" placeholder="主机名">{{ obj.errors.hostname.0 }}</p><p><input type="text" name="ip" placeholder="IP地址">{{ obj.errors.ip.0 }}</p><p><input type="text" name="port" placeholder="端口号">{{ obj.errors.port.0 }}</p><p><input type="text" name="email" placeholder="E-Mail">{{ obj.errors.email.0 }}</p><p><input type="submit" value="登录"></p>
</form>
还有一个问题是,你一点提交你之前在input里填的内容会被清空。这样不好。
自动生成html标签(保留上次输入的信息)
上面讲的都是form组件的一个功能,其实django的form组件主要是完成下面的2个功能的
form组件的2大功能:
- 验证(显示错误信息)
- 保留用户上次输入的信息,通过自动生成的html标签实现
自动生成input标签
用下面的方法可以自动生成input标签。form标签这里加上了一个novalidate属性,是为了禁用客户端的表单验证功能,可以直接看到服务端返回的验证信息。换句话说,就是自动生成的标签还帮我么加上了简单的客户端的初步验证的功能:
<form novalidate action="/host/" method="POST">{% csrf_token %}<p>{{ obj.hostname.label_tag }}{{ obj.hostname }}{{ obj.errors.hostname.0 }}</p><p>{{ obj.ip.label_tag }}{{ obj.ip }}{{ obj.errors.ip.0 }}</p><p>{{ obj.port.label_tag }}{{ obj.port }}{{ obj.errors.port.0 }}</p><p>{{ obj.email.label_tag }}{{ obj.email }}{{ obj.errors.email.0 }}</p><p><input type="submit" value="登录"></p>
</form>
上面只是生成了input标签,里面没有设置placeholder,如果需要加上自定义属性后面会讲。
这里还有这些常用的变量:
- {{ obj.hostname.label_tag }} :自动生成标签,里面是这个样子的
<label for="id_hostname">Hostname:</label>
,一个for一个value,可以用下面2个变量单独拿到这2个值。 - {{ obj.hostname.label }} :标签的value值
- {{ obj.hostname.id_for_label }} :input标签的id,就是label标签里for的值,有这个可以自己写label标签了
- {{ obj.hostname.errors }} :错误信息,貌似和{{ obj.errors.hostname.0 }}是一样的,只是存在不同的位置
这里处理函数要注意,因为GET请求也需要返回obj对象(但是GET里不用填参数)给页面了,所以要做如下的修改:
def host(request):if request.method == 'GET':obj = FM() # 这里也需要创建一个对象,因为需要它生成标签,但是不需要传参数return render(request, 'host.html', {'obj': obj})if request.method == 'POST':obj = FM(request.POST) # 实例化,把POST的数据传入res = obj.is_valid() # 获取结果if res:print(obj.cleaned_data)return HttpResponse('OK')else:# return HttpResponse(str(obj.errors))return render(request, 'host.html', {'obj': obj})
自动生成表单
还是推荐用上面的,这个可定制性太差了。
form标签自己写,submit自己写,其他的都不用写。有3中方式:
- {{ obj.as_p }} :p标签。
- {{ obj.as_ul }} :生成的是li标签,所以外面应该再包一层ul标签?
- {{ obj.as_table }} :生成table的tbody标签,所以外面得自己再包一层table标签。
<form novalidate action="/host/" method="POST">{% csrf_token %}{{ obj.as_p }}<p><input type="submit" value="登录"></p>
</form>
<form novalidate action="/host/" method="POST">{% csrf_token %}{{ obj.as_ul }}<p><input type="submit" value="登录"></p>
</form>
<form novalidate action="/host/" method="POST">{% csrf_token %}<table>{{ obj.as_table }}</table><p><input type="submit" value="登录"></p>
</form>
HTML插件(widgets)
继续看form组件的2大功能:
- 验证(显示错误信息),forms.CharField,负责验证
- 保留用户上次输入的信息,forms.CharField内部包含一个插件widget,负责生成html标签。设了公有属性,成员属性默认参数是None,所以没定义都是取默认的公有属性
插件可以定义标签的类型,默认是文本框,可以改变成多行文本、单选复选,总之是所有的input标签的类型。
插件还可以定义标签的属性,这样就实现了自定义样式。
定义前先要导入插件的模块,from django.forms import widgets
from django.forms import Form # 这个是要继承的类
from django.forms import fields # 学到这里,字段导入这个模块
from django.forms import widgets # 这个模块是插件
class FM(forms.Form):# 变量的字段名匹配form表单里的name属性的值,必须一样hostname = fields.CharField(max_length=12,min_length=6,error_messages={'required': "设备名不能为空",'max_length': "设备名太长,不能超过12",'min_length': "设备名太短,不能小于6"})ip = fields.GenericIPAddressField(widget=widgets.Textarea, # 这里改成多行文本,不定义样式后面就不用跟括号protocol='ipv4')port = fields.IntegerField(widget=widgets.TextInput(attrs={'class': 'c1'}) # 后面跟括号,写上你要自定义的属性)email = fields.EmailField(error_messages={'required': "邮箱不能为空", 'invalid': "邮箱格式错误"})
学到这里,之前导入模块的方式不太好,明确导入需要的模块。
导入 from django.forms import Form
模块,这个是要继承的类
导入 from django.forms import fields
模块来定义字段,这个 fields 是所有字段类型的基类。开始用的是 forms.CharField
都改成 fields.CharField
。
导入 from django.forms import widgets
模块来定义插件。如果只定义类型,后面就不用加括号。如果需要自定义属性,就在后面加括号,attrs里以字典的形式写上各种自定义的属性。
密码的input框类型,可以用这个插件 widget=widgets.PasswordInput,
。
所有的验证类型
查看文件 django\forms\fields.py ,里面有这么多种验证类型。第一个Field是基类,后面的都是继承这个Field的子类或者是孙子类:
__all__ = ('Field', 'CharField', 'IntegerField','DateField', 'TimeField', 'DateTimeField', 'DurationField','RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField','BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField','ComboField', 'MultiValueField', 'FloatField', 'DecimalField','SplitDateTimeField', 'GenericIPAddressField', 'FilePathField','SlugField', 'TypedChoiceField', 'TypedMultipleChoiceField', 'UUIDField',
)
内置字段
创建Form类时,主要涉及到【字段】和【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML。
Django内置字段如下:
- Field
- required=True,是否允许为空,默认都是必填的,有非必填项置为False
- widget=None,HTML插件
- label=None,用于生成Label标签或显示内容,自定义label标签的value的内容,不包括最后的一个冒号
- label_suffix=None,Label内容后缀,自定义label标签的后缀,应该是拼接到所有label后面生成页面显示的label的value的值,默认是英文的冒号
- initial=None,初始值,可以为标签设置初始的内容
- help_text='',帮助信息(在标签旁边显示)
- error_messages=None,自定义错误信息 {'required': '不能为空', 'invalid': '格式错误'}
- show_hidden_initial=False,是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)。就是在你的标签的旁边在生成一个隐藏的一模一样的标签,你可以比较用户的标签相对于之前的状态是否变化了。
- validators=[],自定义验证规则,这里导入一个错误异常,比如RegexValidator。写上自定义的正则表达式和错误信息,可以按自定义的规则进行验证。这是个列表,可以定义多个规则,后面有例子。
- localize=False,是否支持本地化。比如默认的时间都是UTC时间,设置了本地化,就直接显示的是本地的时间了
- disabled=False, 是否可以编辑。
- 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),自定义正则进行验证。和在 fields.CharField 里定义validators是一样的。
- regex,自定制正则表达式
- max_length=None,最大长度
- min_length=None,最小长度
- error_message=None,忽略,错误信息使用 error_messages={'invalid': '...'}
- EmailField(CharField)
- FileField(Field)
- allow_empty_file=False,是否允许空文件
- 使用时注意1:form表单中 enctype="multipart/form-data"
- 使用时注意2:view函数中 obj = MyForm(request.POST, request.FILES)
- ImageField(FileField)
- 需要PIL模块,pip3 install Pillow
- 使用时的注意点同FileField
- URLField(Field)
- BooleanField(Field)
- NullBooleanField(BooleanField)
- ChoiceField(Field),单选返回值为字符串,多选返回值为列表
- choices=(),选项,如:choices = ((0,'上海'),(1,'北京'),)
- required=True,是否必填
- widget=None,插件,默认
widget=widgets.Select
,还可以SelectMultiple(复选select),widgets.RadioSelect(单选),CheckboxSelectMultiple(多选) - 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= "",空值的默认值
- ComboField(Field)
- fields=(),使用多个验证。如下,验证最大长度20,又验证邮箱格式
fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
- MultiValueField(Field)
- 抽象类,只能被继承。子类中可以实现聚合多个字典去匹配一个值,要配合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。protocol必须为both才能启用
- SlugField(CharField),数字,字母,下划线,减号(连字符)
- UUIDField(CharField),uuid类型
内置字段的一些例子
Field 属性 validators,自定义验证规则
from django.forms import Form
from django.core.validators import RegexValidator
class MyForm(Form):mobile = fields.CharField(validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],)
FilePathField(ChoiceField)
把你把一个文件夹下的所有的文件列举出来,然后选一个提交:
from django.forms import Form
from django.forms import fields
class FM(Form):folder = fields.FilePathField(path='app01')
def folder(request):obj = FM()return render(request, 'folder.html', {'obj': obj})
模板语言会自动生成下拉列表:
{{ obj.folder }}
页面上会生成一个下拉框,里面显示的是文件名,后面有个提交按钮。提交的是文件路径。
内置插件
下面这些都是内置的插件。插件后面都可以加上 (attrs={'key': 'value'})
进行自定制属性:
- 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
初始化数据
在Web应用程序中开发编写功能时,时常用到获取数据库中的数据并将值初始化在HTML中的标签上。
获取数据后把数据放在一个字典里。如果是数据库查询,ORM可以直接获取字典形式的数据。
之前GET请求里用的是 obj = FM()
,相当于传入空值,现在可以把字典作为参数传入 obj = FM(initial=dic)
。这样页面上使用Form生成的html标签里就有字典里的值了。
直接修改之前的例子,先准备好一个有数据的字典(这里就不查数据库了)。然后只需要传入这个字典就好了,别的都不用改。这样生成的页面里的输入框是会把字典里的值填上的:
# 实际使用的时候,通过ORM可以直接获取到一条记录的字典
dic = {'hostname': "HOST1",'ip': '192.168.2.1','port': 23,'email': 'py@dj.cn'
}
def host(request):if request.method == 'GET':# obj = FM() # 之前是不传参数的,所有输入框里都是空的obj = FM(initial=dic) # 现在传入参数,输入框里就会把字典里的值作为默认值填上return render(request, 'host.html', {'obj': obj})if request.method == 'POST':obj = FM(request.POST) # 实例化,把POST的数据传入res = obj.is_valid() # 获取结果if res:print(obj.cleaned_data)return HttpResponse('OK')else:# return HttpResponse(str(obj.errors))return render(request, 'host.html', {'obj': obj})
直接像这样 obj = FM(dic)
传字典也是可以的,或者这样 obj = FM({'hostname': "HOST1", 'ip': '192.168.2.1',})
。至少效果是一样的。这里看着按位置参数传递的话,参数不是传给initial的。
序列化操作(待补充)
上面都是用form表单来提交的。之前还学过用Ajax提交。提交没有问题,但是Ajax提交返回的要求是字符串。或者是字典、列表可以用JSON序列化为字符串。但是这里返回的是 obj = FM()
对象,如何序列化成字符串返回给Ajax?
转载于:https://blog.51cto.com/steed/2104396
Python自动化开发学习22-Django下(Form)相关推荐
- Python自动化开发学习的第十一周----WEB框架--Django基础
WEB框架的本质 对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端. #!/usr/bin/env python #coding:utf-8imp ...
- Python自动化开发学习的第九周----线程、进程、协程
一.计算机操作系统的简介 手工操作(无操作系统) 1946年第一台计算机诞生--20世纪50年代中期,还未出现操作系统,计算机工作采用手工操作方式. 程序员将对应于程序和数据的已穿孔的纸带(或卡片)装 ...
- Python自动化开发学习13-堡垒机开发
堡垒机介绍 很多人觉得,堡垒机就是跳板机,这是不全面的.跳板功能只是堡垒机所具备的功能的其中一项.堡垒机还有以下两个至关重要的功能: 权限管理 : 用户使用堡垒机账号登录堡垒机系统.不需要知道别的主机 ...
- Python自动化开发学习22-Django上
session 上节已经讲了使用Cookie来做用户认证,但是 Cookie的问题 缺点:敏感信息不适合放在cookie里,敏感信息只能放在服务器端 优势:把部分用户数据分散的存放在每个客户端,减轻服 ...
- Python自动化开发学习6
引子 假设我们要在我们的程序里表示狗,狗有如下属性:名字.品种.颜色.那么可以先定义一个模板,然后调用这个模板生成各种狗. def dog(name,d_type,color):data = {'na ...
- Python自动化开发学习的第十一周----WEB基础(jquery)
jQuery jQuery 库 - 特性 jQuery 是一个 JavaScript 函数库. jQuery 极大地简化了 JavaScript 编程. jQuery 库包含以下特性: HTML 元素 ...
- Python自动化开发学习15-css补充内容
上节回顾 上一节学习的内容,有一下几点,可以注意一下.或者说推荐这么做. class可以设置多个值-css样式重用 可以给一个标签设置多个class值,这样我们可以为每个class应用一种样式.标签有 ...
- python自动化办公都能做什么菜-Python自动化开发学习之三级菜单制作
本文实例为大家分享了Python三级菜单展示的具体代码,供大家参考,具体内容如下 作业需求: (1)运行程序输出第一级菜单 (2)选择一级菜单某项,输出二级菜单,同理输出三级菜单 (3)让用户选择是否 ...
- Python自动化开发学习3
函数 通过函数,可以定义一段代码块,之后通过函数名可以反复调用 定义一个函数: def alert():"打印Hello World"print("Hello World ...
最新文章
- 昨天又帮爸DIY了一台工作电脑
- 胆固醇竟是新冠最隐蔽同伙!协助病毒入侵细胞,普林斯顿、哈佛医学院首次观察到全过程...
- CentOS Squid 安装配置
- 干货 | 阿里巴巴HBase高可用8年抗战回忆录
- 这是一个沙雕题II(思维好题)
- python自然语言处理案例-Python自然语言处理 NLTK 库用法入门教程【经典】
- 解决问题的策略-分而治之
- 犹豫了几个月,我还是跳槽了....
- php json字符串转json对象一直出错,显示空
- smc数显压力表设定方法_自动增压泵不停止工作原因及解决方法
- c语言50个小程序,C语言50小程序.doc
- 摘来的 esri 的 js 的 一些东西 来自ESRI中国社区 by xiaoyaohu
- JAVA项目--银行管理系统
- bex5 mysql_Bex5开发技巧之MYSQL Incorrect string value
- 学习python的微信公众号_Python学习一:微信公众号验证
- 高度坍塌的几种解决方法
- Python词云_自定义图片做背景
- TypeScript——理解 infer 关键字!
- ValueError: A 0.7-series setuptools cannot be installed with distribute.
- 当店办理层颁发声暗称偶然冲犯西邦己的豪情