知识储备:HTML表单form学习
表单,在前端页面中属于最常见的一个东西了。基本上网站信息的提交都用到了表单,所以下面来学习Django中优雅的表单系统:Form
表单的主要作用是在网页上提供一个图形用户页面,用作采集和提供用户输入数据。
表单的基本结构:
1,from表单常用属性
- action :表单提交的服务器地址,也就是服务器接收表单数据的url地址
- method:表单提交数据的方法(一般为get/post)
- name:最好是name属性的唯一性
- enctype:表单数据提交时使用的编码类型,默认使用"pplication/x-www-form-urlencoded",如果使用post请求,则请求头中的content-type指定值就是该值。如果表单中有上传文件,编码需要使用"multipart/form-data"类型才能完成传递文件数据。
1.1 提交方式 get和post
get:使用URL传参:http://服务器地址?name1 = value&name2=value2(?表示传递参数,?后面采用name=value的形式传递,多个参数之间,用&链接)URL传参不安全,所有信息可在地址栏看到,并且可以通过地址栏随机传递其他数据。URL传递数据量有限,只能传递少量数据。
post:使用HTTP请求传递数据。URL地址栏不可见,比较安全。且传递数据量没有限制。
表单提交中中get和post方式的区别
- 1,get是从服务器获取数据,post是向服务器传送数据
- 2,get安全性非常低,post安全性非常高
- 3,get传送的数据量较小,不能大于2kb,post传送的数据量较大,一般被默认为不受限制。但是理论上,IIS4中最大量为80KB,IIS5中为100KB。
- 4,对于get方式,服务器端用request.QueryString获取变量的值,对于post方式,服务器端用Request.Form获取提交的数据。
- 5,get请求不用添加{% csrf_token %},也不会报csrftoken的错
- 6,post请求的话,就需要添加{ % csrf_token %}标签,而且需要使用 $.ajax()方法,将token传递到服务端。
1.2 浏览器提交表单时,会执行如下步骤
- 1,识别出表单中表单元素的有效项,作为提交项
- 2,构建出一个表单数据集
- 3,根据form表单中的enctype属性的值作为content-type对数据进行编码
- 4,根据form表单中的action属性和method属性向指定的地址发送数据
2,input标签
input标签是输入框,是表单中最重要的部分。
2.1 表单的type属性:
name:是指名字,因为提交的是键值对,所以必须要指定名字,否则无法提交,即使提交了也没有意义。
value:文本框的内容,一般用在不能输入的类型中,如改变按钮的名字等。
placeholder:占位内容,通常用于显示
readonly:只读模式,设置后无法修改输入框的内容
disabled:禁用状态
size:由于输入框是单行的,所以只能设置宽度
maxlength:限制输入框最大输入的字符个数
2.1 表单提交方式
开发中表单提交是很常见的,表单的提交方式也有很多种。
1,使用submit按钮提交表单
2,使用button按钮提交表单
3,使用js进行表单提交,将form表单进行标记,将form表单中的某个元素设置成点击事件,点击时候调用js函数,再用JS。
$("#id").submit()
3,label 标签—— for 属性
label元素不会向用户呈现任何特殊效果。不过,它为鼠标用户改进了可用性。如果您在label元素内点击文本,就会触发此控件。也就是说,当用户选择该标签时,浏览器就会自动将焦点转到和标签相关的表单控件上。
3.1 实例
带有两个输入字段和相关标记的简单HTML表单:
Title
4,Form表单名的功能
- 自动生成HTML表单元素
- 检查表单数据的合法性
- 回显功能(验证错误,输入数据不会被清空)
- 数据类型转换(转换成相应的python类型)
表单提交数据就是由HTML表单向后台传递信息,后台通过request.GET() 或者request.POST()获取。
Django:表单字段和插件widgets
一,Django内建常用字段
字段类型的作用:
- 决定数据库中对应列的数据类型(例如:INTEGER ,VARCHAR,TEXT)
- HTML中对应的表单标签的类型,例如
- 在admin后台和自动生成的表单中最小的数据验证需求
Django内置了许多字段类型,他们都位于django.db.models中,例如models.CharField 。这些类型基本满足需求。如果还不够你也可以自定义字段。
class BooleanField(**kwargs)
- 默认的Widget:CheckboxInput
- 空值:False
- 规范化为:Python 的True 或 False。
- 如果字段带有required=True,验证值是否为True(例如复选框被勾上)。
- 错误信息的键:required
class CharField(**kwargs)
- 默认的Widget:TextInput
- 空值:’ ‘(一个空字符串)
- 规范化为:一个Unicode 对象。
- 如果提供,验证max_length 或min_length。 否则,所有的输入都是合法的。
- 错误信息的键:required, max_length, min_length
- 可选参数:max_length, min_length
class ChoiceField(**kwargs)
- 默认的Widget:Select
- 空值:”(一个空字符串)
- 规范化为:一个Unicode 对象。
- 验证给定的值在选项列表中存在。
- 错误信息的键:required, invalid_choice
- 可选参数:choices
class TypedChoiceField(**kwargs)
- 默认的Widget:Select
- 空值:empty_value
- 规范化为:coerce 参数类型的值。
- 验证给定的值在选项列表中存在并且可以被强制转换。
- 错误信息的键:required, invalid_choice
- 可选参数:coerce,empty_value
class DateField(**kwargs)
- 默认的Widget:DateInput
- 空值:None
- 规范化为:一个Python datetime.date 对象。
- 验证给出的值是一个datetime.date、datetime.datetime 或指定日期格式的字符串。
- 错误信息的键:required, invalid
- 可选参数:input_formats
class DateTimeField(**kwargs)
- 默认的Widget:DateTimeInput
- 空值:None
- 规范化为:一个Python datetime.datetime 对象。
- 验证给出的值是一个datetime.date、datetime.datetime 或指定日期格式的字符串。
- 错误信息的键:required, invalid
- 可选参数:input_formats
class DecimalField(**kwargs)
- 默认的Widget:当Field.localize 是False 时为NumberInput,否则为TextInput。
- 空值:None
- 规范化为:一个Python decimal。
- 验证给定的值为一个十进制数。忽略前导和尾随的空白。
- 错误信息的键:required, invalid, max_value, min_value, max_digits, max_decimal_places, max_whole_digits
- 四个可选的参数:max_value,min_value,max_digits,decimal_places
class DurationField(**kwargs)
- 默认的Widget:TextInput
- 空值:None
- 规范化为:一个Python timedelta。
- 验证给出的值是一个字符串,而可以给转换为timedelta。
- 错误信息的键:required, invalid.
class EmailField(**kwargs)
- 默认的Widget:EmailInput
- 空值:”(一个空字符串)
- 规范化为:一个Unicode 对象。
- 验证给出的值是一个合法的邮件地址,使用一个适度复杂的正则表达式。
- 错误信息的键:required, invalid
class FileField(**kwargs)
- 默认的Widget:ClearableFileInput
- 空值:None
- 规范化为:一个UploadedFile 对象,它封装文件内容和文件名为一个单独的对象。
- 可以验证非空的文件数据已经绑定到表单。
- 误信息的键:required, invalid, missing, empty, max_length
class FilePathField(**kwargs)
- 默认的Widget:Select
- 空值:None
- 规范化为:一个Unicode 对象。
- 验证选择的选项在选项列表中存在。
- 错误信息的键:required, invalid_choice
- 它接受三个额外的参数;只有path 是必需的:
- path,你想要列出的目录的绝对路径。这个目录必须存在。
recursive,如果为False(默认值),只用直接位于path 下的文件或目录作为选项。如果为True,将递归访问这个目录,其所有的子目录和文件都将作为选项。
match,正则表达式表示的一个模式;只有匹配这个表达式的名称才允许作为选项。
allow_files,可选。为True 或False。默认为True。表示是否应该包含指定位置的文件。它和allow_folders 必须有一个为True。
allow_folders,可选。为True 或False。 默认为False。表示是否应该包含指定位置的目录。 它和allow_files 必须有一个为True。
class FloatField(**kwargs)
- 默认的Widget:当 Field.localize 是False 时为NumberInput,否则为TextInput。
- 空值:None
- 规范化为:一个Float 对象。
- 验证给出的值是一个浮点数。和Python 的float() 函数一样,允许前导和尾随的空白符。
- 错误信息的键:required, invalid, max_value, min_value
class ImageField(**kwargs)
- 默认的Widget:ClearableFileInput
- 空值:None
- 规范化为: An UploadedFile object that wraps the file content and file name into a single object.
- 验证文件数据已绑定到表单,并且该文件具有Pillow理解的图像格式。
- 错误信息的键:required, invalid, missing, empty, invalid_image
class IntegerField(**kwargs)
- 默认的Widget:当Field.localize 是False 时为NumberInput,否则为TextInput。
- 空值:None
- 规范化为:一个Python 整数或长整数。
- 验证给定值是一个整数。允许前导和尾随空格,如Python的int()函数。
- 错误信息的键:required, invalid, max_value, min_value
- 可选参数:max_value,min_value
class GenericIPAddressField(**kwargs)
- 包含IPv4或IPv6地址的字段。
- 默认的Widget:TextInput
- 空值:”(一个空字符串)
- 规范化为:一个Unicode 对象。 IPv6地址如下所述进行归一化。
- 验证给定值是有效的IP地址。
- 错误信息的键:required, invalid
- 可选参数:protocol,unpack_ipv4
class MultipleChoiceField(**kwargs)
- 默认的Widget:SelectMultiple
- 空值:[](一个空列表)
- 规范化为:一个Unicode 对象列表。
- 验证给定值列表中的每个值都存在于选择列表中。
- 错误信息的键:required, invalid_choice, invalid_list
class TypedMultipleChoiceField(**kwargs)
就像MultipleChoiceField,除了TypedMultipleChoiceField需要两个额外的参数,coerce和empty_value。
- 默认的Widget:SelectMultiple
- 空值:empty_value
- 规范化为:coerce参数提供的类型值列表。
- 验证给定值存在于选项列表中并且可以强制。
- 错误信息的键:required, invalid_choice
class NullBooleanField(**kwargs)
- 默认的Widget:NullBooleanSelect
- 空值:None
- 规范化为:一个Python True, False 或None 值。
- 不验证任何内容(即,它从不引发ValidationError)。
class RegexField(**kwargs)
- 默认的Widget:TextInput
- 空值:”(一个空字符串)
- 规范化为:一个Unicode对象
- 验证给定值与某个正则表达式匹配。
- 错误信息的键:required, invalid
class SlugField(**kwargs)
- 默认的Widget:TextInput
- 空值:None
- 规范化为:一个Python 的datetime.time 对象。
- 验证给定值是datetime.time或以特定时间格式格式化的字符串。
- 错误信息的键:required, invalid
class TimeField(**kwargs)
- 默认的Widget:TextInput
- 空值:None
- 规范化为:一个Python 的datetime.time 对象。
- 验证给定值是datetime.time或以特定时间格式格式化的字符串。
- 错误信息的键:required, invalid
class URLField(**kwargs)
- 默认的Widget:URLInput
- 空值:”(一个空字符串)
- 规范化为:一个Unicode 对象。
- 验证给定值是有效的URL。
- 错误信息的键:required, invalid
class UUIDField(**kwargs)
- 默认的Widget:TextInput
- 空值:”(一个空字符串)
- 规范化为:一个UUID 对象。
- 错误信息的键:required, invalid
- 复杂点的内建Field类
class ComboField(**kwargs)
- 默认的Widget:TextInput
- 空值:”(一个空字符串)
- 规范化为:一个Unicode 对象。
- 根据指定为ComboField的参数的每个字段验证给定值。
- 错误信息的键:required, invalid
class MultiValueField(fields=(), **kwargs)
- 默认的Widget:TextInput
- 空值:”(一个空字符串)
- 规范化为:子类的compress方法返回的类型。
- 针对指定为MultiValueField的参数的每个字段验证给定值。
- 错误信息的键:required, invalid, incomplete
class SplitDateTimeField(**kwargs)
- 默认的Widget:SplitDateTimeWidget
- 空值:None
- 规范化为:一个Python datetime.datetime 对象。
- 验证给定的值是datetime.datetime或以特定日期时间格式格式化的字符串。
- 错误信息的键:required, invalid, invalid_date, invalid_time
- 处理关系的字段
两个字段可用于表示模型之间的关系:ModelChoiceField和ModelMultipleChoiceField。这两个字段都需要单个queryset参数,用于创建字段的选择。在表单验证时,这些字段将把一个模型对象(在ModelChoiceField的情况下)或多个模型对象(在ModelMultipleChoiceField的情况下)放置到cleaned_data表单的字典。
class ModelChoiceField(**kwargs)
- 默认的Widget:Select
- 空值:None
- 规范化为:一个模型实例。
- 验证给定的id存在于查询集中。
- 错误信息的键:required, invalid_choice
class ModelMultipleChoiceField(**kwargs)
- 默认的Widget:SelectMultiple
- 空值:QuerySet (self.queryset.none())
- 规范化为: 模型实例的一个QuerySet。
- 验证在给定的值列表中的每个id存在于查询集中。
- 错误信息的键:required, list, invalid_choice, invalid_pk_value
- 创建自定义的字段
如果内建的字段不能满足你的需求,你可以很容易地创建自定义的字段。你需要创建django.forms.Field 的一个子类。它只要求实现一个clean() 方法和接收上面核心参数的init() 方法(required, label, initial, widget, help_text)。
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类型 ...
1.1 字段命名约束
字段是模型中最重要的内容之一,也是唯一必须的部分。字段在Python中表现为一个类属性,体现了数据表中的一个列,请不要使用 clean , save , delete 等Django内置的模型API名字,防止命名冲突。下面是一个展示,注意字段的写法:
from django.db import models class Musician(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) instrument = models.CharField(max_length=100) class Album(models.Model): artist = models.ForeignKey(Musician, on_delete=models.CASCADE) name = models.CharField(max_length=100) release_date = models.DateField() num_stars = models.IntegerField()
Django不允许下面两种字段名:
- 与Python关键字冲突。这会导致语法错误,例如:
class Example(models.Model): pass = models.IntegerField() # 'pass'是Python保留字!
- 字段名中不能有两个以上下划线在一起,因为两个下划线是Django的查询语法,例如:
class Example(models.Model): foo__bar = models.IntegerField() # 'foo__bar' 有两个下划线在一起!
由于你可以自定义表名,列名,上面的规则可能被绕开,但是请养成良好的习惯,一定不要那么起名。
SQL语言的join,where和Select 等保留字可以作为字段名,因为Django对他们都进行了转义。
二,django模型字段常用参数
2.1,null
如果是True,Django会在数据库中将次字段的值置为NULL,默认值为False。(null=True是允许数据库这个值是空)
2.2,blank
如果为True时,django的Admin添加数据时可允许空值,可以不填。如果为False,则必须填。默认为False。(black=True 表示允许表单验证为空)
2.3,primary_key = False
主键,对AutoFiled设置主键后,就会代替原来的自增ID列
2.4,auto_now 和 auto_now_add
auto_now 自动创建——无论添加或者修改,都是当前操作的时间
auto_now_add 自动创建——永远是创建时的时间
2.5,choices
一个二维的元组被用作choices,如果这样定义,Django会select box代替普通的文本框,并且限制choices的值时元组中的值。
2.6,max_length
字段长度
2.7,default
默认值
2.8,verbose_name
Admin中字段的显示名称,如果不设置该参数时,则与属性名
2.9,db_column
数据库中的字段名称
2.10,unique = True
不允许重复
2.11,db_index = True
数据库索引
2.12,editable = True
在Admin里是否可以编辑
2.13,error_messages = None
错误提示
2.14,auto_created = Flase
自动创建
三,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
四,常用选择插件——widget
widget是form表单最重要的参数之一,指定渲染Widget时使用的widget类,举个例子:就是说这个form字段在HTML页面中为文本输入框,密码输入框,单选框,多选框。。。。。
4.1,密码输入框
pwd = forms.CharField( min_length=6, label="密码", widget=forms.widgets.PasswordInput()
4.2,单radioSelect
单radio值为字符串
user_type_choice = ( (0, u'普通用户'), (2, u'高级用户'), ) user_type = forms.IntegerField(initial=2, widget=forms.widgets.RadioSelect(choices=user_type_choice,))
4.3,单选select
user_type_choice = ( (0, u'普通用户'), (2, u'高级用户'), ) user_type = forms.IntegerField(initial=2, widget=forms.widgets.Select(choices=user_type_choice,))
4.4,多选select
user_type_choice = ( (0, u'普通用户'), (2, u'高级用户'), ) user_type = forms.IntegerField(initial=[1, ], widget=forms.widgets.SelectMultiple(choices=user_type_choice,))
4.5,单选checkbox
user_type = forms.CharField(widget=forms.widgets.CheckboxInput())
4.6,多选checkbox
值为列表
user_type_choice = ( (0, u'普通用户'), (2, u'高级用户'), ) user_type = forms.CharField( initial=[2, ], widget=forms.widgets.CheckboxSelectMultiple( choices=user_type_choice, ))
4.7 关于choice的注意事项
在使用选择标签的时候,需要注意choices的选项可以从数据库获取,但是由于是静态子弹,获取的值无法更新,那么需要自定义构造方法从而达到目的。
5,如何上传文件和图片的方法
5.1,FileField
class FileField(upload_to=None, max_length=100, **options)[source]
上传文件字段(不能设置为主键)。默认情况下,该字段在HTML中表现为一个ClearableFileInput 标签。在数据库内,我们实际保存的是一个字符串类型,默认最大长度为100,可以通过max_length 参数自定义。真实的文件是保存在服务器的文件系统内的。
重要参数upload_to 用于设置上传地址的目录和文件名。如下例所示:
class MyModel(models.Model): # 文件被传至`MEDIA_ROOT/uploads`目录,MEDIA_ROOT由你在settings文件中设置 upload = models.FileField(upload_to='uploads/') # 或者 # 被传到`MEDIA_ROOT/uploads/2015/01/30`目录,增加了一个时间划分 upload = models.FileField(upload_to='uploads/%Y/%m/%d/')
Django很人性化的帮我们实现了根据日期生成目录的方式!
upload_to 参数也可以接收一个回调函数,该函数返回具体的路径字符串,如下例:
def user_directory_path(instance, filename): #文件上传到MEDIA_ROOT/user_/ 目录中 return 'user_{0}/{1}'.format(instance.user.id, filename) class MyModel(models.Model): upload = models.FileField(upload_to=user_directory_path)
例子中,user_directory_path 这种回调函数,必须接收两个参数,然后返回一个Unix风格的路径字符串。参数instace代表一个定义了FileField的模型的实例,说白了就是当前数据记录。filename是原本的文件名。
5.2,ImageField
class ImageField(upload_to=None, height_field=None, width_field=None, max_length=100, **options)[source]
用于保存图像文件袋额字段。其基本用法和特征与FileField一样,只不过多了两个属性height和width。默认清洗下,该字段在HTML中表现为一个ClearableFileInput 标签。在数据库内,我们实际保存的是一个字符串类型。默认最大长度100,可以通过max_length 参数自定义。真实的图片是保存在服务器的文件系统内的。
- height_field参数:保存有图片高度信息的模型字段名
- width_field参数:保存有图片宽度信息的模型字段名
使用Django的ImageField需要提前安装pillow模块。
使用FileField或者ImageField字段的步骤:
- 1,在settings文件中宏,配置MEDIA_ROOT,作为你上传文件在服务器中的基本路径(为了性能考虑,这些文件不会被存储在数据库中)。再配置个MEDIA_URL ,作为公用URL,指向上传文件的基本路径。请确保Web服务器的用户账号对该目录具有写的权限。
- 2,添加FileField或者ImageField字段到你的模型中,定义好upload_to参数,文件最终会放在MEDIA_ROOT目录的“upload_to”子目录中。
- 3,所有真正被保存在数据库中的,只是指向你上传文件路径的字符串而已。可以通过url属性,在Django的模板中方便的访问这些文件。例如,假设你有一个ImageField字段,名为mug_shot,那么在Django模块的HTML文件中,可以使用{{ object.mug_shot.url }} 来获取该文件。其中的object 用你具体的对象名称代替。
- 4,可以通过name 和 size 属性,获取文件的名称和大小信息。
安全建议
无论你如何保存上传的文件,一定要注意他们的内容和格式,避免安全漏洞!务必对所有上传文件进行安全检查,确保他们不出问题! 如果你不加任何检查就盲目的让任何人上传到你的服务器文档根目录内,比如上传了一个CGI 或者PHP脚本,很可能就会被访问的用户执行,这具有致命的危害。
5.3,FilePathField
class FilePathField(path=None, match=None, recursive=False, max_length=100, **options)[source]
一种用来保存文件路径信息的子段。在数据表内以字符串的形式存在,默认最大长度100,可以通过max_length参数设置。
他们包含有下面的一些参数:
- path:必须指定的参数。表示一个系统绝对路径
- match:可选参数,一个正则表达式,用于过滤文件名。只匹配基本文件名,不匹配路径。例如foo.*\.txt$ ,只匹配文件名 foo23.txt,不匹配bar.txt 与 foo23.png 。
- recursive:可选参数,只能是True或者FALSE。默认为FALSE。决定是否包含子目录,也就是是否递归的意思。
- allow_files:可选参数,只能是True或者False。默认为True,决定是否应该将文件名包括在内。它和allow_folders其中,必须有一个为True。
- allow_folders:可选参数,只能是True或者Flase。默认为False,决定是否应该将目录名包括在内。
比如:
FilePathField(path="/home/images", match="foo.*", recursive=True)
他只匹配/home/images/foo.png ,但是不匹配 /home/images/foo/bar.png ,因为默认情况,只匹配文件名,而不管路径怎么样的。
5.4,UUIDField
数据库无法为自己生成uuid,因此需要如下使用default参数:
import uuid # Python的内置模块 from django.db import models class MyUUIDModel(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) # 其它字段
5.5,上传文件实例
views.py
from django.shortcuts import render, HttpResponse import os def upload(request): if request.method == 'GET': # return HttpResponse("OK") return render(request, 'user8_book/upload.html') elif request.method == 'POST': # print("OK") # return HttpResponse("OK") obj = request.FILES.get('file') print(obj.name) f = open(os.path.join('static/upload', obj.name), 'wb') for line in obj.chunks(): f.write(line) f.close() return HttpResponse('上传成功')
urls.py
from django.conf.urls import url, include from django.contrib import admin from user8_book import views from django.urls import path urlpatterns = [ path('book/', views.book), path('upload.html/', views.upload), ]
models.py
from django.db import models # Create your models here. class Upload(models.Model): text = models.CharField(max_length=100) file = models.FileField(max_length=128)
upload.html
upload {# 文件上传的form#} {#
5.6,上传文件报错及其解决方法
报错内容:
解决方法:
查看自己的HTML文件中,表单的action属性值结尾是否添加了“/”,我就是因为没有添加,找了半天错误,真尴尬。粗心大意
Django:Form表单(验证,定制错误信息,Select)
1,Django from流程
- 1,创建类,继承form.Form
- 2,页面根据类的对象自动创建HTML标签
- 3,提交,request.POST 封装到类的对象里,obj=UserInfo(request.POST)
- 4,用户输入是否合法 obj.is_valid()
- 5,全部合法,获取全部内容 obj.claen()
- 6,只有有不合法 obj.errors
2,Form表单使用建议
我们可以在APP目录下新建立一个myforms.py文件,然后再建立form。为什么要新建一个myforms.py呢?其好处显而易见:
- 所有的表单在一个文件里,非常便于后期维护,比如增添或者修订字段。
- myform.py可以通过clean方法自定义表单验证,非常便捷,不用在views.py里再进行表单验证(比如检查用户是否已经存在等等),逻辑更加清晰
3,定制表单
一般情况下,使用一种东西就会受制于一种东西,但是这并不是Django的意愿。并非你接受了django表单的方便就需要接受这么丑陋的页面。Django表单时可以随你心愿定制的。
我们不但可以对标签名进行定制化,还可以限制输入的位数,比如下面代码,我们限制了输入九位,第十位就无法输入,而且并非所有的信息都是强制填的,可以根据需要来抉择,同样看下面代码,即使email空着不填,表单也能正常提交。
class UserInfo(forms.Form): email = forms.EmailField(required=False) host = forms.CharField(max_length=9) port = forms.CharField(max_length=4) mobile = forms.CharField(max_length=11)
4,美化表单
每个表单字段都有一个对应的Widget class,它对应一个HTML表单Widget,我们可以使用Forms中的widget来对我们的前端表按照我们的要求进行改造
4.1 示例1:长文本输入框
比如当我们需要做一个长文本输入框时候:
class UserInfo(forms.Form): email = forms.EmailField(required=False) host = forms.CharField(max_length=9) port = forms.CharField(max_length=4) mobile = forms.CharField(max_length=11) comment = forms.CharField(widget=forms.Textarea)
html代码如下:
Title
效果如下:
4.2 示例二:下拉框
当我们想做一个下拉框的时候:
其views.py代码如下:
from django.shortcuts import render # Create your views here. from django import forms from django.forms.widgets import SelectDateWidget class UserInfo(forms.Form): email = forms.EmailField(required=False) host = forms.CharField(max_length=9) port = forms.CharField(max_length=4) mobile = forms.CharField(max_length=11) comment = forms.CharField(widget=forms.Textarea) years = ('2010', '2019', '2029') year = forms.DateField(widget=SelectDateWidget(years=years)) def user_list(request): obj = UserInfo() if request.method == 'POST': user_input_obj = UserInfo(request.POST) if user_input_obj.is_valid(): # 如果输入合法的话,就获取输入的值 data = user_input_obj.clean() print(data) else: # 获取错误信息 error_msg = user_input_obj.errors return render(request, 'user15_form/user_list.html', {'obj':user_input_obj, 'errors':error_msg}) return render(request, 'user15_form/user_list.html', {'obj':obj})
HTML代码如下:
Title
结果展示如下:
4.3 示例三:自己定制输入框
对ChoiceField字段,Form默认的是集成select的widget,即使用HTML的列表形式
views.py 的代码如下(这里只修改了comment的代码):
comment = forms.CharField(widget=forms.TextInput(attrs={'size':'40'}))
我们为其设置了attrs,也就是HTML中的css属性。(要是想为整个表单或者更深度的定制一些css以及JavaScript,可以前往Django官方文档了解)
效果如下:
5,Form表单字段
5.1 Field.clean(value)
虽然字段类主要使用在表单类中,但是我们也可以直接实例化他们来使用,以便更好地了解他们是如何工作的。每个字段实例都有一个clean()方法,它接受一个参数,然后返回“清洁的”数据或者抛出一个django.forms.
>>> from django import forms >>> f = forms.EmailField() >>> f.clean('[email protected]') '[email protected]' >>> f.clean('invalid email address') Traceback (most recent call last): ... ValidationError: ['Enter a valid email address.']
每个字段类的构造函数至少接受这些参数。有些字段类接受额外的,字段特有的参数,但以下参数应该总能接受:
5.2 Field.required
默认情况下,每个字段都假设必须有值,所以如果你传递一个空的值——不管是None,还是空字符串(" ")——clean() 都将引发一个ValidationError异常。
>>> from django import forms >>> f = forms.CharField() >>> f.clean() Traceback (most recent call last): File "", line 1, inTypeError: clean() missing 1 required positional argument: 'value' >>> f.clean("") Traceback (most recent call last): File "", line 1, in File "C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages\django\forms\fields.py", line 148, in clean self.validate(value) File "C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages\django\forms\fields.py", line 126, in validate raise ValidationError(self.error_messages['required'], code='required') django.core.exceptions.ValidationError: ['This field is required.'] >>> f.clean("12") '12'
5.3 Field.label
正如前面“输出表单为HTML”中解释的,字段默认label是通过将字段名中所有的下划线转换成空格并大写第一个字母生成的。如果默认的标签不合适,可以指定label。
>>> from django import forms >>> class CommentForm(forms.Form): ... name = forms.CharField(label='Your name') ... url = forms.URLField(label='Your Web site', required=False) ... comment = forms.CharField() >>> f = CommentForm(auto_id=False) >>> print(f)Your name: Your Web site: Comment:
5.4 Field.label_suffix
label_suffix参数让你基于每个字段覆盖表单的label_suffix
>>> class ContactForm(forms.Form): ... age = forms.IntegerField() ... nationality = forms.CharField() ... captcha_answer = forms.IntegerField(label='2 + 2', label_suffix=' =') >>> f = ContactForm(label_suffix='?') >>> print(f.as_p())
5.5 Field.initial
initial 参数让你指定渲染未绑定的表单中的字段时使用的初始值。
5.6 Field.widget
widget参数让你指定渲染表单时使用的Widget类,更多信息参见上面。
5.7 Filed.help_text
5.8 Field.error_messages
error_messages 参数让你覆盖字段引发的异常中的默认信息。传递的是一个字典,其键为你想覆盖的错误信息。
5.9 Field.validators
validators 参数让你可以为字段提供一个验证函数的列表。
5.10 Field.localize
localize 参数启用表单数据的本地化,包括输入和输出。
5.11 Field.has_changed()
has_changed() 方法用于决定字段的值是否从初始值发送了变化。返回True或者False。
6,form关于clean及其cleaned_data的说明
django表单验证中比较有用的就是clean和cleaned_data了。
clean是在is_valid()内部调用的,cleaned_data主要用来检查字段是否符合定义的格式,读取表单返回的值,如果是则返回类型为字段dict型。
比如email = cleaned_data['email'] 读取name为“email”的表单提交值,并赋予email变量。
cleaned_data中的值类型与字段定义的Field类型一致。如果字段定义charfield,那么clean方法返回的cleaned_data中对应的字段值就是字符型,定义为ModelChoiceField,那么cleaned_data中字段值是某个model实例。定义为ModelMultipleChoiceField,则cleaned_data中字段值是model实例list。
Django:表单实例
首先我们看一下下面的案例:
#/usr/bin/env python #-*- coding:utf-8 -*- from django.shortcuts import render # Create your views here. def user_list(request): host = request.POST.get('host') port = request.POST.get('port') mail = request.POST.get('mail') mobile = request.POST.get('mobile') #这里有个问题,如果,这个from表单有20个input,你在这里是不是的取20次? #验证: #输入不能为空,并且有的可以为空有的不可以为空 #如果email = 11123123 这样合法吗? #如果mobile = 11123123 这样合法吗? #如果ip = 11123123 这样合法吗? ''' 你在这里是不是需要做一大堆的输入验证啊?并且有很多这种页面会存在这种情况,如果每个函数都这样做估计就累死了 ''' return render(request,'user_list.html')
针对于上面的问题,如何解决呢?——那就是Form表单
Django中form表单的作用:
- 1,自动生成HTML表单元素
- 2,用来做用户提交的验证
- 3,检查表单数据的合法性
- 4,回显功能(验证错误,输入数据不会被清空)
- 5,数据类型转换(转换成相应的python类型)
通常提交表单数据就是由HTML表单向后台传递信息,后台通过request.GET() 或者 request.POST()获取。
一,构建一个简单表单的流程,并提交信息给数据库
1.1,建立一个django项目
建一个Blog项目,并在template下新建两个html页面,一个注册页面命名为register,一个欢迎页面为welcome。
1,创建project django-admin startproject Blog 2,创建APP python manage.py startapp user1 3,修改settings配置 在INSTALLED_APPS中添加APP:user1 在TEMPLATES中查看“DIRS”内容,如果有template,请保持原样,如果没有,则添加 'DIRS': [os.path.join(BASE_DIR, 'templates')]
项目目录如下:
1.2, 填充HTML文件内容
给template下两个html页面register和welcome填充内容。
register.html
register
welcome.html
welcome welcome to this page
1.3,在models中创建表结构
在user1/models.py中创建表结构,代码如下:
from django.db import models # Create your models here. class BlogUser(models.Model): username = models.CharField(max_length=200, unique=True) password = models.CharField(max_length=200)
ChariField 字符串字段,用于较短的字符串,需要max_length来指定VARCHAR数据库字段的大小。
同时,修改Bolg/settings.py的内容:
找到下面这段代码,并注释掉: DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } 然后写入下面代码: import pymysql pymysql.install_as_MySQLdb() DATABASES = { 'default': { # 这里可以指定使用的数据库类型,例如mysql 'ENGINE': 'django.db.backends.mysql', 'NAME': 'blog_demo', 'USER':'root', 'PASSWORD':'******', 'HOST':'localhost', 'PORT':'3306', } }
如果这里不熟悉请参考:Django连接MySQL数据库
并且,映射数据库,这一步不能忘记:
在终端创建表 1,生成同步数据库的代码: python manage.py makemigrations 2,同步数据库(也就是对数据库执行真正的迁移动作): python manage.py migrate
1.4,以创建对象的方式操作数据库
我们重构views.py中的代码
from django.shortcuts import render # Create your views here. from user1.models import BlogUser def register(request): if request.method =='GET': return render(request, 'register.html') elif request.method == 'POST': bloguser = BlogUser() bloguser.username = request.POST.get('username') bloguser.password = request.POST.get('password') bloguser.save() return render(request, 'welcome.html')
get() 中的username和password是取的register 里的username的值。以获取(request)注册的信息,保存为save()。
然后运行项目,并注册信息,代码如下:
1, 运行项目 python manage.py runserver 2,在浏览器中输入: http://127.0.0.1:8000/user/register/
然后我们注册信息,并提交。我们可以看到上面的register.html中用户名是唯一的,所以当我们注册相同的名称时候回报错500,并且传输失败。
下面在数据库看一下我们的注册信息
其中,3,5不显示,因为我输入了相同的用户名。如果输入不重复,则显示下面界面。
二,构建一个简单表单的流程,了解Form知识
在上面的基础上,我们再实现一个表单流程,继续了解form表单的知识。
2.1,首先创建对应的视图函数 views.py内容如下:
from django.shortcuts import render, HttpResponse # Create your views here. from django import forms class LoginForm(forms.Form): account = forms.CharField() password = forms.CharField() email = forms.CharField() def login(request): if request.method == "POST": form = LoginForm(request.POST) if form.is_valid(): return HttpResponse("登录成功") else: form = LoginForm() return render(request, 'user6/login.html',{'form':form})
2.2,建立对应的模板 login.html
Title
然后,去配置urls一些基本的配置(比如模板,路由等)。
我们直接点开login.html 内容如下:
我们打开Django项目,从127.0.0.1:8000/user6/login/ 进入,如下:
直接访问地址就显示出这样一个简单的界面,由HTML文件可以看到并没有js代码对数据有效性进行验证,我们随机输入账号,密码,邮箱,则提交,显示登陆成功。如下:
三,定制表单
从上面的代码我们发现,前端一个 {{ form }} 就能做出一个完整强大的表单。但是我们只能用account password 做名称吗?
不是的,这里我们可以定制其名字,并且可以限制输入位数等等各种操作。
3.1,如何给form表单设置自定义报错内容
在form里有一个参数:error_messages 在他这里就可以定义报错内容
class UserInfo(forms.Form): # required是否可以为空,如果为False,则说明可以为空 email = forms.EmailField(required=False,error_messages={'required':u'邮箱不能为空'}) # 如果required 不写默认为Ture host = forms.CharField(error_messages={'required':u'主机不能为空'}) port = forms.CharField(error_messages={'required':u'端口不能为空'}) mobile = forms.CharField(error_messages={'required':u'手机不能为空'})
3.2,如何给form表单添加一个属性
class UserInfo(forms.Form): # required是否可以为空,如果为False,则说明可以为空 email = forms.EmailField(required=False,error_messages={'required':u'邮箱不能为空'}) # 如果required 不写默认为Ture host = forms.CharField(error_messages={'required':u'主机不能为空'}) port = forms.CharField(error_messages={'required':u'端口不能为空'}) mobile = forms.CharField(error_messages={'required':u'手机不能为空'}, # 这里默认是TextInput 标签 widget = forms.TextInput(attrs={'class':'form-control', 'placeholder':u'手机号码'}))
3.3,如何给form表单添加一个备注
class UserInfo(forms.Form): # required是否可以为空,如果为False,则说明可以为空 email = forms.EmailField(required=False,error_messages={'required':u'邮箱不能为空'}) # 如果required 不写默认为Ture host = forms.CharField(error_messages={'required':u'主机不能为空'}) port = forms.CharField(error_messages={'required':u'端口不能为空'}) mobile = forms.CharField(error_messages={'required':u'手机不能为空'}, # 这里默认是TextInput 标签 widget = forms.TextInput(attrs={'class':'form-control', 'placeholder':u'手机号码'})) # 我们再这里新增一个备份 memo = forms.CharField(required=False, widget=forms.Textarea(attrs={'class':'form-control','placeholder':u'备份'}))
同样的HTML代码也要新增:
3.4,自定义正则表达式增加判断规则
import re from django import forms from django.core.exceptions import ValidationError def mobile_validate(value): # 正则匹配 mobile_re = re.compile(r'^(13[0-9]|15[0123456789]|18[0-9]|14[57])[0-9]{8}$') if not mobile_re.match(value): raise ValidationError("手机号码格式错误") class UserInfo(forms.Form): # required是否可以为空,如果为False,则说明可以为空 email = forms.EmailField(required=False, error_messages={'required': u'邮箱不能为空'}) host = forms.CharField(error_messages={'required': u'主机不能为空'}) port = forms.CharField(error_messages={'required': u'端口不能为空'}) mobile = forms.CharField(# 应用我们自己定义的规则 validators=[mobile_validate,], error_messages={'required': u'手机不能为空'}, # 这里默认使用TextInput 标签 widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': u'手机号码'})) # 我们新增一个备份 memo = forms.CharField(required=False, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': u'备份'})) def user_list(request): # 创建了这个对象 obj = UserInfo() if request.method == 'POST': # 获取用户输入一句话就搞定 user_input_obj = UserInfo(request.POST) # 判断用户输入是否合法 if user_input_obj.is_valid(): # 获取用户输入 data = user_input_obj.clean() print(data) else: # 如果发生错误,捕获异常 # 这里原来什么都没写,默认是ul的样式,默认是as_ul(), # 如果我们写成as_data()返回的就是一个原生的字符串 # 还有一个as_json error_msg = user_input_obj.errors.as_data() # 然后把错误信息返回 print(error_msg) # 然后把对象传给html,在把错误信息传递过去 return render(request, 'user6/register.html', {'obj': obj, 'errors': error_msg,}) # 将对象传给html return render(request, 'user6/register.html', {'obj':obj,})
当我们输入不合法的时候,或者不是email格式的时候,会出现如下错误:
这样后端我们就有一套验证的机制了,就是通过is_valid()来判断用户输入是否合法!如果不合法就把返回的信息发送出去,如果合法获取数据,继续操作即可!
3.5,生成select标签
class UserInfo(forms.Form): user_type_choice = ( (0,u'普通用户'), (1,u'高级用户'), ) user_type = forms.IntegerField(widget=forms.widgets.Select(choices=user_type_choice, attrs={'class':'form-control'})) # required是否可以为空,如果为False,则说明可以为空 email = forms.EmailField(required=False,error_messages={'required':u'邮箱不能为空'}) # 如果required 不写默认为Ture host = forms.CharField(error_messages={'required':u'主机不能为空'}) port = forms.CharField(error_messages={'required':u'端口不能为空'}) mobile = forms.CharField(error_messages={'required':u'手机不能为空'}, # 这里默认是TextInput 标签 widget = forms.TextInput(attrs={'class':'form-control', 'placeholder':u'手机号码'})) # 我们再这里新增一个备份 memo = forms.CharField(required=False, widget=forms.Textarea(attrs={'class':'form-control','placeholder':u'备份'}))
HTML内更改如下:
3.6.关于后端验证
这个后端验证是必须要有验证机制的,前端可以不写但是后端必须写,前端的JS是可以被禁用掉的,在实际的生产环境中比如登录和验证的时候,我们一般都使用 JQuery+AJAX 来判断用户的输入是否为空,假如JS被禁用(浏览器端可以设置禁用JS效果)的话,我们这个认证屏障是不是就消失了呢?(虽然一般不会禁用但是还是存在风险),所以我们一般做两种认证,在前端做一遍认证,在后端做一遍认证。
3.7,代码总结
import re from django import forms 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 UserInfo(forms.Form): user_type_choice = ( (0, u'普通用户'), (1, u'高级用户'),) user_type = forms.IntegerField(widget=forms.widgets.Select(choices=user_type_choice,attrs={'class':'form-control'})) email = forms.EmailField(required=True,error_messages={'required':u'邮箱不能为空'}) #required是否可以为空,如果为False说明可以为空 host = forms.CharField(error_messages={'required':u'主机不能为空'}) #如果required不写默认为Ture port = forms.CharField(error_messages={'required':u'端口不能为空'}) #默认mobile里有一个默认为空的机制,我们在原有的参数里增加怎们自定义的方法 mobile = forms.CharField(validators=[mobile_validate,],#应用咱们自己定义的规则 error_messages={'required':u'手机不能为空'}, widget=forms.TextInput(attrs={'class':'form-control','placeholder':u'手机号码'}) #这里默认是TextInput,标签 ) #咱们在新增一个备注 memo = forms.CharField(required=False, widget=forms.Textarea(attrs={'class':'form-control','placeholder':u'备注'})) def user_list(request): obj = UserInfo() #创建了这个对象 if request.method == 'POST': #获取用户输入一句话就搞定 user_input_obj = UserInfo(request.POST) if user_input_obj.is_valid(): #判断用户输入是否合法 data = user_input_obj.clean() #获取用户输入 print data else: #如果发生错误,捕捉错误 error_msg = user_input_obj.errors.as_data()#这里原来什么都没写,默认是ul的样式,默认是as_ul(),如果我们写成as_data()返回的就是一个原生的字符串 #还有一个as_json print error_msg #打印一下然后看下他的类型 #然后把错误信息返回 return render(request,'user_list.html',{'obj':obj,'errors':error_msg,})#然后把对象传给html,在把错误信息传递过去 return render(request,'user_list.html',{'obj':obj,})#然后把对象传给html
参考文献:https://blog.csdn.net/qq_42068900/article/details/80904596
https://blog.csdn.net/qq_37049781/article/details/79283547