Forms和ModelForm

  • 进行数据校验,先看数据校验的过程
      注册页面图解:
前端为了用户体验会做一些校验,不满足校验要求会报错
服务端也会对数据进行一些校验,不满足校验要求会报错
数据库也会对数据进行一些校验,不满足校验要求会报错 form组件和modleform组件就是让我们的数据校验过程更加简单一些,功能非常强大

Forms组件

  • 提供了三个功能
      1.能够帮我们生成HTML标签
2.标签中保留之前用户输入的数据
3.数据校验

forms组件的使用流程

  • 1.在views.py文件中创建一个自定义的form类
      from django import forms   #需要先导入这个forms

      class RegisterForm(forms.Form):
phone = forms.CharField()
username = forms.CharField()
password = forms.CharFied() #用label参数可以将HTML中的label标签中内容显示为中文
phone = forms.CharField(label='手机号')
username = forms.CharField(label='用户名')
password = forms.CharField(label='密码')
  • 2.在views.py文件中写上视图函数
      def register(request):
if request.method == 'GET':
form = RegisterForm() #将上面的form类进行实例化
return render(request,'register.html',{'form':form})#将实例化后的对象返回到html中生成对应的属性的标签
  • 3.创建一个html文件,比如叫作register.html,内容如下
      <!DOCTYPE html>
<html lang='en'>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body> <h1>注册页面</h1>
<form action='/register/' method='post' novalidate> #novalidate取消浏览器自带的错误提示
{% csrf_token %}
<div>
<!--{{ form.phone.id_for_label }}相当于绑定下面生成的input标签中的id属性 -->
<!-- {{ form.phone.label }}相当于把后台自定义的中文渲染到标签中 -->
<label for="{{ form.phone.id_for_label }}">{{ form.phone.label }}</label>
<!-- {{form.phone}}相当于生成这个input标签<input type="text" name="phone" required="" id="id_phone"> -->
{{ form.phone }}
</div> <div>
<label for="{{ form.username.id_for_label }}">{{ form.username.label }}</label>
{{ form.username }}
</div> <div>
<label for="{{ form.password.id_for_label }}">{{ form.password.label }}</label>
{{ form.password }}
</div> <input type='submit'>
</form>
</body>
</html>

保留原数据和校验功能

  • forms组件代码
      class RegisterForm(forms.Form):
#每个字段,默认都有一个required=True的参数,表示该字段数据不能为空
#phone = form.CharField(label='手机号',required=True)
phone = forms.CharField(
label='手机号',
required=True,
#错误提示信息的自定制
error_messages={
'required':'小敏敏提示您,不能为空!',
}
)
username = forms.CharField(label='用户名')
password = forms.CharField(label='密码')
  • views.py内容如下
      def register(request):
if request.method == 'GET':
form = RegisterForm()
return render(request,'register.html',{'form':form})
else:
print(request.POST)
form = RegisterForm(data=request.POST)
#把数据交给了RegisterForm,那么在通过form来渲染标签时,就将原来的的数据以默认值的形式(value='值')生成在了对应标签上
if form.is_valid(): #执行校验,如果所有数据都校验通过了,返回True,但凡有一个数据没有通过校验,返回False
print('通过校验的数据',form.cleaned_data)
return HttpResponse('ok')
print('错误信息>>>',form.errors)
return render(request,'register.html',{'form':form})
  • register.html内容
      <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
<!-- 给错误信息定制一些css样式 -->
.error_msg{
font-size:12px;
color:red;
}
</style>
</head>
<body> <h1>注册页面</h1>
<!-- form标签的这个novalidate属性,就是把浏览器自带的必填提示信息去掉 -->
<form action='/register/' method='post' novalidate>
{% csrf_token %}
<!-- {{ form.errors }} 表示存放着所有字段的错误信息 -->
<div>
<label for='{{ form.phone.id_for_label }}'>{{ form.phone.label }}</label>
{{ form.phone }}
<!-- form.phone.errors.0 表示展示一个错误信息 -->
<span class='error_msg'>{{ form.phone.errors.0 }}</span>
</div> <div>
<label for='{{ form.username.id_for_label }}'>{{ form.username.label }}</label>
{{ form.username }}
<span class="error_msg">{{ form.username.errors.0 }}</span>
</div> <div>
<label for='{{ form.password.id_for_label }}'>{{ form.password.label }}</label>
{{ form.password }}
<span class='error_msg'>{{ form.password.errors.0 }}</span>
</div> <input type='submit'> </body>
</html>

Form常用字段属性与插件

  • initial 初始值
      #生成input标签有个默认值
username = fomrs.CharField(label='用户名',initial='小红')
  • widget 插件的意思,能够定制我们的标签显示效果
      示例1  密文输入框
password = forms.CharField(
label='密码',
#CharField默认插件是TextInput相当于type='text',下面相当于修改type='password',完整写法forms.widgets.PasswordInput,下面是简写
widget = forms.PasswordInput,
) 示例2 给标签加上一些其他属性
password = forms.CharField(
label='密码',
#attrs={'标签属性':'属性值'}
widget=forms.PasswordInput(attrs={'class':'c1 c2','placeholder':'请输入密码'}),
)
  • 生成单选下拉框 ChoiceField默认插件是widget=forms.Select
      #sex = forms.fields.ChoiceField()  fields可以不用写
sex = forms.ChoiceField(
choices =((1,'女'),(2,'男')),
#(1,'女')生成一个option标签,value值为1,文本内容为女
label ='性别',
# initial =2 #初始值
)
  • 单选radio框 widget=forms.RadioSelect
      sex = forms.ChoiceField(
choices=((1,'女'),(2,'男')),
label='性别',
#initial =2,
#widget=forms.Select, #ChoiceField的默认插件
#input,type='radio'的单选框
#widget=forms.RadioSelect,
#修改插件,并设置属性
widget=forms.RadioSelect(attrs={'class':'c1'})
)
  • 多选下拉框 MultipleChoiceField
      hobby = forms.MultipleChoiceField(
choices=((1,'喝酒'),(2,'抽烟'),(3,'励志')),
label='爱好'
)
  • 多选checkbox框 widget=forms.CheckboxSelectMultiple
      hobby = forms.MultipleChoiceField(
choices=((1,'喝酒'),(2,'抽烟'),(3,'励志')),
label = '爱好',
#widget=forms.CheckboxInput, #做单选功能的复(多)选框形式,必须勾选协议的那个选框
widget=forms.CheckboxSelectMultiple,
)
  • 单选功能的复选框形式,像那种勾选同意协议的那个选框 widget=forms.CheckboxInput 里面choices只有true或false
      status = forms.MultipleChoiceField(
choices=(('True',同意),('Flase',不同意))
label='同意是否勾选协议',
widget=forms.CheckboxInput,
)
  • date类型
      bday = forms.CharField(
label='生日',
widget=forms.TextInput(attrs={'type':'date'}), 只要设置一个date属性就可以了
)

单选或者多选框使用数据库中的数据

  • 方式1 forms.ModelChoiceField
      models中的模型类
class Publish(models.Model):
name = models.CharField(max_length=32) def __str__(self):
return self.name form类中的字段写法
#生成选择框,并使用数据库中的数据 必须要用这个ModelChoiceField
publish = forms.ModelChoiceField(
queryset=models.Publish.objects.all(), #必须是queryset
)
会自动生成单选下拉框,并且option标签value属性对应的值,是queryset参数对应的queryset集合里面的models模型类对象的id值,option标签的文本内容是每个models模型类对象。
#生成标签的时候,页面显示第一层会有个自带的'--------',第二个往后才是数据
  • 方式2 forms.ChoiceField
      publish = forms.ChoiceField(
choices=models.Publish.objects.all().values_list('id','name')
#quertset[(1,'xx出版社'),]
)
#生成标签的时候,没有上面那个自带的'--------',直接就显示的是数据

Form所有内置字段

  • Field
      required=True,            是否不为空
widget=None, HTML插件,设置插件相当于可以更改标签并设置属性
label=None, 用于生成label标签或显示内容
initial=None, 初始值
help_text='', 帮助信息(在标签旁边显示)
error_messages=None, 错误信息{'required':'不能为空',}
validators=[], 自定义验证规则
disabled=False, 是否可以编辑,默认False,可编辑
  • CharField(Field)
      max_length=None,      最大长度
min_length=None, 最小长度
strip=True, 是否移除用户输入空白
  • IntegerField(Field)
      max_value=None,      最大值
min_value=None, 最小值
  • FloatField(Field)
      ...
  • 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)
      error_messages={'invalid':'邮箱格式不正确'}
  • 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类型 复制代码

数据校验功能

      格式不对的错误信息定制
'initial':'邮箱格式不对',

简单校验示例

      form类
class DataForm(forms.Form):
#不能为空,长度不能超过10位,不能短于4位
username = forms.CharField(
label='用户名',
max_length=10,
min_length=4,
error_messages={
'required':'不能为空',
'max_length':'太长啦,受不了',
'min_length':'太短啦,不舒服',
}
) #可以为空
phone = forms.CharField(
label='手机号',
required=False,
)
#要满足邮箱格式,不能为空
email = forms.EmailField(
label='邮箱',
error_messages={
'invalid':'邮箱格式不对', #invalid验证邮箱格式的
}
)
#提交的数据不能超出选项
sex = forms.ChoiceField(
label='性别',
choices=((1,'女'),(2,'男')),
widget=forms.RadioSelect,
)
#form只校验类中写的属性对应的那些数据
#向csrf_token这些不会校验
  • views.py
      def data(request):
if request.method == 'GET':
form = DataForm()
return render(request,'data.html',{'form':form})
else:
form = DataForm(request.POST)
if form.is_valid(): #做校验,都通过返回True
#校验成功之后的数据form.cleaned_data里面不包含没有校验的数据,也就是不会校验form类中没有指定的属性对应的数据
print(form.cleaned_data)# {'username': 'chao', 'phone': '', 'email': 'asdf@xx.com', 'sex': '1'}
return HttpResponse('ok')
else:
print(form.errors)
return render(request,'data.html',{'form':form})
  • data.html文件
      <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title> </head>
<body> <form action="/data/" method="post" novalidate>
{% csrf_token %}
<div>
<label for="{{ form.username.id_for_label }}">{{ form.username.label }}</label>
{{ form.username }}
{{ form.username.errors.0 }}
</div>
<div>
<label for="{{ form.phone.id_for_label }}">{{ form.phone.label }}</label>
{{ form.phone }}
{{ form.phone.errors.0 }}
</div>
<div>
<label for="{{ form.email.id_for_label }}">{{ form.email.label }}</label>
{{ form.email }}
{{ form.email.errors.0 }}
</div>
<div>
<label for="{{ form.sex.id_for_label }}">{{ form.sex.label }}</label>
{{ form.sex }}
{{ form.sex.errors.0 }}
</div> <input type="submit"> </form> </body> </html>

RegexValidator验证器

  • 示例代码
      from django.core.validators import RegexValidator
class DataForm(forms.Form):
#不能为空,长度不能超过10位,不能短于4位
username = forms.CharField(
...
#RegexValidator('正则','不符合正则时的错误信息')
validators=[RegexValidator(r'^a','用户名必须以a开头'),RegexValidator(r'b$','用户名必须以b结尾')]
)

校验函数

  • 示例
      import re
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
#ValidationError是django提供的一个数据校验失败的一个错误类型
#先定义一个函数,函数中我们可以做数据的校验,如果数据校验失败,我们可以raise ValidationError('错误信息')
def mobile_match(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('手机号格式有误') 使用
phone =forms.CharField(
label='手机号',
validators=[mobile_match,], #列表里面写我们自己定义的函数名
)

局部钩子

  • 代码示例
      class DataForm(forms.Form):
#不能为空,长度不能超过10位,不能短于4位
username = forms.CharField(
label='用户名',
max_length=10,
min_length=4,
)
#可以为空
phone = forms.CharField(
required=False,
validators=[mobile_match,],
)
。。。 #局部钩子,能够对username这个字段的数据进行一个更高级的校验
#语法 clean_字段名称
def clean_username(self):
uname = self.cleaned_data.get('username') #cleaned_data.get 获取数据
if '666' in uname:
raise ValidationError('不能光喊6,没有用')
#如果校验通过,需要return这个值
return uname def clean_phone(self):
pass

全局钩子

  • 示例代码
      class DataForm(forms.Form):
#密码
password = forms.CharField()
#再次确认密码
confirm_password=forms.CharField() #多个字段进行比较时,一般都是用全局钩子固定函数名clean
def clean(self):
p1 = self.cleaned_data.get('password')
p2 = self.cleaned_data.get('confirm_password')
if p1 != p2:
#直接raise错误,这个错误信息保存到了全局错误信息里面
#也就是self.errors里面
#所以要用add_error('属性名','错误提示信息')
self.add_error('confirm_password','抱歉,两次密码不一致')
self.add_error('password','抱歉,两次密码不一致') #如果校验通过,必须return self.cleaned_data
return self.cleaned_data

源码部分,主要看cleaned_data,为什么会在没有走视图函数的时候,就能获取数据

       def _clean_fields(self):
for name, field in self.fields.items():
# print(name, field)
# name属性名称,field是CharField()类的实例化对象
# value_from_datadict() gets the data from the data dictionaries.
# Each widget type knows how to retrieve its own data, because some
# widgets split data over several HTML fields.
if field.disabled:
value = self.get_initial_for_field(field, name)
else:
value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
try:
if isinstance(field, FileField):
initial = self.get_initial_for_field(field, name)
value = field.clean(value, initial)
else:
# 将CharField中的max_length,required..
value = field.clean(value)
# 将属性对应的CharField里面的简单校验规则进行了校验
self.cleaned_data[name] = value
#self.cleaned_data['username'] = 'abc'
if hasattr(self, 'clean_%s' % name): #clean_username
value = getattr(self, 'clean_%s' % name)()
self.cleaned_data[name] = value
except ValidationError as e:
self.add_error(name, e)

简单总结

      首先对所有的我们自己定义的类中的属性进行循环,self.fields能够获取到所有的属性,循环过程中先对属性,比如username=form.CharField(max_length=12),里面的参数校验规则进行了校验,这个校验完成之后,给self.clean_data这个字典进行了赋值  self.clead_data['username'] ='sad'
然后对该字段的局部钩子进行了校验,在局部钩子中我们可以拿到self.clean_data['username']这个数据进行处理,别忘了局部钩子校验成功之后,要return这个字段的值,然后才进入下一次循环,处理下一个属性
当上面的所有循环执行完之后,才执行我们的全局钩子,全局钩子中校验成功之后别忘了return self.clean_data

静态文件配置和templates模板配置

      可以在app01/app02应用文件夹下分别创建一个static名称的文件夹,存放自己应用的静态文件
可以在app01/app02应用文件夹下分别创建一个templates名称的文件夹,存放自己应用的html文件 django寻找html文件静态文件的查找顺序是先找总配置中的templates或者
statics文件夹,如果找不到对应文件,就去每个应用下的static或者templates去找对
应文件,顺序是按照settings.py配置文件中的INDSTALL_APP中注册app的顺序进行查
找,找到就返回,不在继续往下找了,所以要注意,最好在static或者templates下面创
建一个以应用名为名称的文件夹,将文件放到这里面,以防文件名冲突导致的问题。

django学习第十四天--Forms和ModelForm的更多相关文章

  1. Django框架(十四)-- forms组件、局部钩子、全局钩子

    一.什么是forms组件 forms组件就是一个类,可以检测前端传来的数据,是否合法. 例如,前端传来的邮箱数据,判断邮件格式对不对,用户名中不能以什么开头,等等 二.forms组件的使用 1.使用语 ...

  2. Django学习之十四:Django ORM继承关系

    目录 Django ORM继承关系 1. SINGLE_TABLE(django好像不支持) 2. TABLE_PER_CLASS 3. JOINED 4. 代理继承 Django ORM继承关系 参 ...

  3. python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例

    python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例 新浪爱彩双色球开奖数据URL:http://zst.aicai.com/ssq/openInfo/ 最终输出结果格 ...

  4. Linux学习之十四、管线命令

    Linux学习之十四.管线命令 地址:http://vbird.dic.ksu.edu.tw/linux_basic/0320bash_6.php

  5. Django学习之十: staticfile 静态文件

    目录 Django学习之十: staticfile 静态文件 理解阐述 静态文件 Django对静态文件的处理 其它方面 总结 Django学习之十: staticfile 静态文件 理解阐述     ...

  6. 风炫安全WEB安全学习第二十四节课 利用XSS钓鱼攻击

    风炫安全WEB安全学习第二十四节课 利用XSS钓鱼攻击 XSS钓鱼攻击 HTTP Basic Authentication认证 大家在登录网站的时候,大部分时候是通过一个表单提交登录信息. 但是有时候 ...

  7. Django笔记二十四之数据库函数之比较和转换函数

    本文首发于公众号:Hunter后端 原文链接:Django笔记二十四之数据库函数之比较和转换函数 这一篇笔记开始介绍几种数据库函数,以下是几种函数及其作用 Cast 转换类型 Coalesce 优先取 ...

  8. Django框架(十五)—— forms组件、局部钩子、全局钩子

    目录 forms组件.局部钩子.全局钩子 一.什么是forms组件 二.forms组件的使用 1.使用语法 2.组件的参数 3.注意点 三.渲染模板 四.渲染错误信息 五.局部钩子 1.什么是局部钩子 ...

  9. Django 学习笔记(四)模板变量

    关于Django模板变量官方网址:https://docs.djangoproject.com/en/1.11/ref/templates/builtins/ 1.传入普通变量 在hello/Hell ...

  10. (C/C++学习笔记) 十四. 动态分配

    十四. 动态分配 ● C语言实现动态数组 C语言实现动态数组,克服静态数组大小固定的缺陷 C语言中,数组长度必须在创建数组时指定,并且只能是一个常数,不能是变量.一旦定义了一个数组,系统将为它分配一个 ...

随机推荐

  1. [转帖]KingbaseES 事务总结

    目录 1. 什么是事务? 2. 事务的属性-ACID 3. 数据库事务的操作方式 3.1. SET TRANSACTION 3.2. BEGIN 3.3. COMMIT 3.4. ROLLBACK 3 ...

  2. [转帖]10--k8s之数据持久化

    https://www.cnblogs.com/caodan01/p/15136217.html 目录 一.emptDir 二.hostPath 三.pv 和 pvc 1.环境准备 2.创建pv 3. ...

  3. Linux上面批量更新SQLSERVER SQL文本文件的办法

    1. 今天同事让帮忙更新几个SQL文件.. 本着自己虽然low 但是不能太low的想法, 简单写一个 shell 脚本来执行. 2. 因为我的linux 里面都安装了 sqlcmd 的工具 所以办法就 ...

  4. Gitlab使用说明

          零.gitlab简介   Gitlab是一个成熟的代码管理工具.为企业和组织提供内部的源代码的存储和管理功能.         一.gitlab角色总览   gitlab中的角色分管理员和 ...

  5. vue/cli的配置详解

    查看vue/cli的配置 vue的脚手架隐藏了所有的webpack相关的配置,若是想要查看webpack的配置 你可以去执行 vue inspect > output.js 这样就可以查看它的配 ...

  6. 【笔记】学到几个 golang 代码小技巧

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 从这篇文章学到:10个令人惊叹的Go语言技巧,让你的代码更 ...

  7. vim 从嫌弃到依赖(16)——宏

    终于到了我第二喜欢的vim功能了(当然了,最喜欢的是.命令).我原本计划在介绍完.命令之后介绍宏,以便让各位小伙伴们能了解到vim对于重复操作进行的强大的优化.但是由于宏本身跟寄存器息息相关,所以还是 ...

  8. Java中YYYY-MM-dd在跨年时出现的bug

    先看一张图: Bug的产生原因: 日期格式化时候,把 yyyy-MM-dd 写成了 YYYY-MM-dd Bug分析: 当时间是2019-08-31时, public class DateTest { ...

  9. springboot多模块打包报错问题根因分析:Unable to find main class

    问题背景: 项目结构为springboot多模块,其中有四个模块bean.utils.user.ems,其中user和ems模块为主程序,包含启动类,其他两个模块为其服务,提供依赖 问题分析: 查看u ...

  10. 多智能体强化学习算法【三】【QMIX、MADDPG、MAPPO】

    相关文章: 常见多智能体强化学习仿真环境介绍[一]{推荐收藏,真的牛} 多智能体强化学习算法[一][MAPPO.MADDPG.QMIX] 多智能体强化学习算法[二][MADDPG.QMIX.MAPPO ...