Django中的forms组件实例详解


Posted in Python onNovember 08, 2018

Form介绍

我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来。

与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否输入,输入的长度和格式等正不正确。如果用户输入的内容有错误就需要在页面上相应的位置显示对应的错误信息.。

Django form组件就实现了上面所述的功能。

总结一下,其实form组件的主要功能如下:

  • 生成页面可用的HTML标签
  • 对用户提交的数据进行校验
  • 保留上次输入内容

先在应用目录下my_forms.py定义好一个UserForm类

from django import forms
 from django.forms import widgets 
   class UserForm(forms.Form):
     username = forms.CharField(min_length=4, label='用户名',
                   widget=widgets.TextInput(attrs={"class": "form-control"}),
                   error_messages={
                     "required": "用户名不能为空",
                   })
     pwd = forms.CharField(min_length=4, label='密码',
                error_messages={
                  "required": "密码不能为空",
                },
                widget=widgets.PasswordInput(attrs={"class": "form-control"}))
     r_pwd = forms.CharField(min_length=4, label='确认密码',
                 widget=widgets.PasswordInput(attrs={"class": "form-control"}),
                 error_messages={
                   "required": "密码不能为空",
                 })
     email = forms.EmailField(label='邮箱',
                 widget=widgets.EmailInput(attrs={"class": "form-control"}),
                 error_messages={
                   "required": '邮箱不能为空',
                   "invalid": "邮箱格式错误",
                 })
     tel = forms.CharField(label='手机号',
                widget=widgets.TextInput(attrs={"class": "form-control"}),
                )

再写一个视图函数:

  在写一个视图函数

def reg(request):
     form = UserForm()
     if request.method == "POST":
       print(request.POST)
       # 实例化form对象的时候,把post提交过来的数据直接传进去
       form = UserForm(request.POST) # form表单的name属性值应该与forms组件的字段名称一致
       if form.is_valid():
         print(form.cleaned_data)
         return HttpResponse('注册成功')
     return render(request, 'reg.html', locals())

login.html

<!DOCTYPE html>
   <html lang="zh_CN">
   <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>注册</title>
   </head>
   <body>
   <h3>传统form表单</h3>
   <form action="" method="post">
     {% csrf_token %}
     <p>用户名:<input type="text" name="username"></p>
     <p>密码:<input type="password" name="pwd"></p>
     <p>确认密码:<input type="password" name="r_pwd"></p>
     <p>邮箱:<input type="email" name="email"></p>
     <p>手机号:<input type="tel" name="tel"></p>
     <p><input type="submit" value="提交"></p>
   </form>
   <h3>forms组件渲染方式1</h3>
   <form action="" method="post" novalidate>
     {% csrf_token %}
     <p>{{ form.username.label }}:{{ form.username }} <span>{{ form.username.errors.0 }}</span></p>
     <p>密码:{{ form.pwd }}
       <span>{{ form.pwd.errors.0 }}</span></p>
     <p>确认密码:{{ form.r_pwd }}
       <span>{{ form.r_pwd.errors.0 }}</span></p>
     <p>邮箱:{{ form.email }}
       <span>{{ form.email.errors.0 }}</span></p>
     <p>手机号:{{ form.tel }}
       <span>{{ form.tel.errors.0 }}</span></p>
     <p><input type="submit" value="提交"></p>
   </form>
   <h3>forms组件渲染标签方式2</h3>
     <form action="" method="post" novalidate>
       {% csrf_token %}
       {% for field in form %}
         <div class="form-group clearfix">
           <label for="">{{ field.label }}</label>
           {{ field }}
           <span style="color: red" class="pull-right">{{ field.errors.0 }}</span>
           {% if field.name == 'r_pwd' %}
             <span style="color: red" class="pull-right">{{ errors.0 }}</span>
           {% endif %}
         </div>
       {% endfor %}
       <input type="submit" value="注册" class="btn btn-default pull-right">
     </form>
   <h3>forms组件渲染标签方式3  不推荐使用</h3>
   <form action="" method="post">
     {% csrf_token %}
     {{ form.as_p }}
     <input type="submit" value="注册">
   </form>
   </body>
   </html>

看网页效果发现 也验证了form的功能:

• 前端页面是form类的对象生成的                                      -->生成HTML标签功能

• 当用户名和密码输入为空或输错之后 页面都会提示        -->用户提交校验功能

• 当用户输错之后 再次输入 上次的内容还保留在input框   -->保留上次输入内容

Form那些事儿

常用字段与插件

创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;

initial

初始值,input框里面的初始值。

class LoginForm(forms.Form):
  username = forms.CharField(
    min_length=8,
    label="用户名",
    initial="张三" # 设置默认值
  )
  pwd = forms.CharField(min_length=6, label="密码")

error_messages

重写错误信息。

class LoginForm(forms.Form):
  username = forms.CharField(
    min_length=8,
    label="用户名",
    initial="张三",
    error_messages={
      "required": "不能为空",
      "invalid": "格式错误",
      "min_length": "用户名最短8位"
    }
  )
  pwd = forms.CharField(min_length=6, label="密码")

password

class LoginForm(forms.Form):
  ...
  pwd = forms.CharField(
    min_length=6,
    label="密码",
    widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True)
  )

radioSelect

单radio值为字符串

class LoginForm(forms.Form):
  username = forms.CharField(
    min_length=8,
    label="用户名",
    initial="张三",
    error_messages={
      "required": "不能为空",
      "invalid": "格式错误",
      "min_length": "用户名最短8位"
    }
  )
  pwd = forms.CharField(min_length=6, label="密码")
  gender = forms.fields.ChoiceField(
    choices=((1, "男"), (2, "女"), (3, "保密")),
    label="性别",
    initial=3,
    widget=forms.widgets.RadioSelect()
  )

单选Select

class LoginForm(forms.Form):
  ...
  hobby = forms.fields.ChoiceField(
    choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
    label="爱好",
    initial=3,
    widget=forms.widgets.Select()
  )

多选Select

class LoginForm(forms.Form):
  ...
  hobby = forms.fields.MultipleChoiceField(
    choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
    label="爱好",
    initial=[1, 3],
    widget=forms.widgets.SelectMultiple()
  )

单选checkbox

class LoginForm(forms.Form):
  ...
  keep = forms.fields.ChoiceField(
    label="是否记住密码",
    initial="checked",
    widget=forms.widgets.CheckboxInput()
  )

多选checkbox

class LoginForm(forms.Form):
  ...
  hobby = forms.fields.MultipleChoiceField(
    choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
    label="爱好",
    initial=[1, 3],
    widget=forms.widgets.CheckboxSelectMultiple()
  )

关于choice的注意事项:

在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的。

方式一:

from django.forms import Form
from django.forms import widgets
from django.forms import fields
 
class MyForm(Form):
  user = fields.ChoiceField(
    # choices=((1, '上海'), (2, '北京'),),
    initial=2,
    widget=widgets.Select
  )
  def __init__(self, *args, **kwargs):
    super(MyForm,self).__init__(*args, **kwargs)
    # self.fields['user'].choices = ((1, '上海'), (2, '北京'),)
    # 或
    self.fields['user'].choices = models.Classes.objects.all().values_list('id','caption')

方式二:

from django import forms
from django.forms import fields
from django.forms import models as form_model
class FInfo(forms.Form):
  authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all()) # 多选
  # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all()) # 单选
Django Form所有内置字段
Field
  required=True,        是否允许为空
  widget=None,         HTML插件
  label=None,         用于生成Label标签或显示内容
  initial=None,        初始值
  help_text='',        帮助信息(在标签旁边显示)
  error_messages=None,     错误信息 {'required': '不能为空', 'invalid': '格式错误'}
  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 Form内置字段

校验

方式一:

from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator
class MyForm(Form):
  user = fields.CharField(
    validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
  )

方式二:

import re
from django.forms import Form
from django.forms import widgets
from django.forms import fields
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 PublishForm(Form):
  title = fields.CharField(max_length=20,
              min_length=5,
              error_messages={'required': '标题不能为空',
                      'min_length': '标题最少为5个字符',
                      'max_length': '标题最多为20个字符'},
              widget=widgets.TextInput(attrs={'class': "form-control",
                             'placeholder': '标题5-20个字符'}))
  # 使用自定义验证规则
  phone = fields.CharField(validators=[mobile_validate, ],
              error_messages={'required': '手机不能为空'},
              widget=widgets.TextInput(attrs={'class': "form-control",
                             'placeholder': u'手机号码'}))
  email = fields.EmailField(required=False,
              error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'},
              widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))

方式三:

def clean(self):
     pwd = self.cleaned_data.get('pwd')
     r_pwd = self.cleaned_data.get('r_pwd')
     print(pwd, r_pwd)
     if pwd and r_pwd:
       if pwd == r_pwd:
         return self.cleaned_data
       raise ValidationError('两次密码不一致')
     else:
       return self.cleaned_data
   def clean_username(self):
     val = self.cleaned_data.get('username')
     user = UserInfo.objects.filter(name=val)
     if not user:
       return val
     else:
       raise ValidationError("用户名已注册")
   def clean_tel(self):
     val = self.cleaned_data.get('tel')
     if len(val) == 11:
       return val
     raise ValidationError('手机号格式错误')

总结:

'''
   局部钩子:
     1、is_valid()
     2、self.errors
     3、self.full_clean()
     4、self._clean_fields() 校验每一个字段
    5、for 循环每一个字段名、验证规则对象
     6、
       验证通过:
         value = field.clean(value)
         self.cleaned_data[name] = value       添加到clean_data中
         利用反射、钩子自定义验证规则
         hasattr(self, 'clean_%s' % name)      写自定义方法
         value = getattr(self, 'clean_%s' % name)() 执行自定义方法
           pass:
             self.cleaned_data[name] = value   添加到clean_data中
           no_pass:
             raise ValidationError(msg)     抛出异常
       不通过:
         raise ValidationError(msg)
   全局钩子:
     1、is_valid()
     2、self.errors
     3、self.full_clean() 
     4、self._clean_form()  全部字段调用完成之后 调用
     5、self.clean() 重写clean方法
     6、全局钩子错误:forms.errors.get("__all__")
   '''

总结

以上所述是小编给大家介绍的Django中的forms组件实例详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Python 相关文章推荐
windows下安装python paramiko模块的代码
Feb 10 Python
python应用程序在windows下不出现cmd窗口的办法
May 29 Python
python处理csv数据的方法
Mar 11 Python
Python基于PycURL自动处理cookie的方法
Jul 25 Python
Python字符串、元组、列表、字典互相转换的方法
Jan 23 Python
Python Sqlite3以字典形式返回查询结果的实现方法
Oct 03 Python
python中比较两个列表的实例方法
Jul 04 Python
python实现ip代理池功能示例
Jul 05 Python
python 实现波浪滤镜特效
Dec 02 Python
python 通过使用Yolact训练数据集
Apr 06 Python
解决Pytorch半精度浮点型网络训练的问题
May 24 Python
Python中使用ipython的详细教程
Jun 22 Python
python微元法计算函数曲线长度的方法
Nov 08 #Python
python实现简单的单变量线性回归方法
Nov 08 #Python
python/sympy求解矩阵方程的方法
Nov 08 #Python
python生成lmdb格式的文件实例
Nov 08 #Python
python实现嵌套列表平铺的两种方法
Nov 08 #Python
python用列表生成式写嵌套循环的方法
Nov 08 #Python
在Python中实现shuffle给列表洗牌
Nov 08 #Python
You might like
让PHP支持断点续传的源码
2010/05/16 PHP
ThinkPHP模板自定义标签使用方法
2014/06/26 PHP
Laravel解决nesting level错误和隐藏index.php的问题
2019/10/12 PHP
MSN消息提示类
2006/09/05 Javascript
php图像生成函数之间的区别分析
2012/12/06 Javascript
简介JavaScript中的setDate()方法的使用
2015/06/11 Javascript
使用 stylelint检查CSS_StyleLint
2016/04/28 Javascript
JS获取屏幕高度的简单实现代码
2016/05/24 Javascript
nodejs实现邮件发送服务实例分享
2017/03/29 NodeJs
vue实现微信分享朋友圈,发送朋友的示例讲解
2018/02/10 Javascript
在vue中使用公共过滤器filter的方法
2018/06/26 Javascript
js中的数组对象排序分析
2018/12/11 Javascript
使用Vue父子组件通信实现todolist的功能示例代码
2019/04/11 Javascript
Vue页面刷新记住页面状态的实现
2019/12/27 Javascript
[03:17]2014DOTA2 国际邀请赛中国区预选赛 四强专访
2014/05/23 DOTA
在Python中使用sort()方法进行排序的简单教程
2015/05/21 Python
Python字符串格式化
2015/06/15 Python
python编码总结(编码类型、格式、转码)
2016/07/01 Python
浅谈Python类里的__init__方法函数,Python类的构造函数
2016/12/10 Python
python中使用正则表达式的连接符示例代码
2017/10/10 Python
Flask框架各种常见装饰器示例
2018/07/17 Python
自定义实现 PyQt5 下拉复选框 ComboCheckBox的完整代码
2020/03/30 Python
用HTML5.0制作网页的教程
2010/05/30 HTML / CSS
开发人员所需要知道的HTML5性能分析面面观
2012/07/05 HTML / CSS
Html5 Canvas动画基础碰撞检测的实现
2018/12/06 HTML / CSS
HTML5 直播疯狂点赞动画实现代码 附源码
2020/04/14 HTML / CSS
Radley英国官网:英国莱德利小狗包
2019/03/21 全球购物
市场营销个人求职信范文
2014/02/02 职场文书
毕业生自荐信如何写
2014/03/24 职场文书
教研处工作方案
2014/05/26 职场文书
学校中层领导培训心得体会
2016/01/11 职场文书
积极心理学课程心得体会
2016/01/22 职场文书
幼师必备:幼儿园期末教师评语50条
2019/11/01 职场文书
apache基于端口创建虚拟主机的示例
2021/04/24 Servers
MYSQL(电话号码,身份证)数据脱敏的实现
2021/05/28 MySQL
将MySQL的表数据全量导入clichhouse库中
2022/03/21 MySQL