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 相关文章推荐
Python天气预报采集器实现代码(网页爬虫)
Oct 07 Python
python抓取网页图片并放到指定文件夹
Apr 24 Python
详解Python中with语句的用法
Apr 15 Python
Python实现把json格式转换成文本或sql文件
Jul 10 Python
使用Python简单的实现树莓派的WEB控制
Feb 18 Python
Python 内置函数complex详解
Oct 23 Python
Python实现简单的多任务mysql转xml的方法
Feb 08 Python
python中urlparse模块介绍与使用示例
Nov 19 Python
Python实现求一个集合所有子集的示例
May 04 Python
基于python3抓取pinpoint应用信息入库
Jan 08 Python
Python之关于类变量的两种赋值区别详解
Mar 12 Python
Python eval函数原理及用法解析
Nov 14 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中使用Oracle数据库(2)
2006/10/09 PHP
给初学PHP的5个入手程序
2006/11/23 PHP
晋城吧对DiscuzX进行的前端优化要点
2010/09/05 PHP
一个PHP缓存类代码(附详细说明)
2011/06/09 PHP
thinkPHP5框架auth权限控制类与用法示例
2018/06/12 PHP
JSDoc 介绍使用规范JsDoc的使用介绍
2011/02/12 Javascript
Javascript Request获取请求参数如何实现
2012/11/28 Javascript
jcrop基本参数一览
2013/07/16 Javascript
使用jQuery重置(reset)表单的方法
2014/05/05 Javascript
js实现缓冲运动效果的方法
2015/04/10 Javascript
ndm:NPM的桌面GUI应用程序
2018/10/15 Javascript
Node.js fs模块(文件模块)创建、删除目录(文件)读取写入文件流的方法
2019/09/03 Javascript
JavaScript中的各种宽高属性的实现
2020/05/08 Javascript
vue 重塑数组之修改数组指定index的值操作
2020/08/09 Javascript
[01:00:06]加油DOTA_EP01_网络版
2014/08/09 DOTA
[45:52]完美世界DOTA2联赛PWL S3 Forest vs INK ICE 第二场 12.09
2020/12/12 DOTA
PyQt5实现无边框窗口的标题拖动和窗口缩放
2018/04/19 Python
浅谈python中对于json写入txt文件的编码问题
2018/06/07 Python
django admin后台添加导出excel功能示例代码
2019/05/15 Python
pytorch 固定部分参数训练的方法
2019/08/17 Python
使用python代码进行身份证号校验的实现示例
2019/11/21 Python
python with语句的原理与用法详解
2020/03/30 Python
浅谈sklearn中predict与predict_proba区别
2020/06/28 Python
PyCharm中关于安装第三方包的三个建议
2020/09/17 Python
Python类型转换的魔术方法详解
2020/12/23 Python
纯css3实现图片翻牌特效
2015/03/10 HTML / CSS
瑞典网上购买现代和复古家具:Reforma
2019/10/21 全球购物
运动会致辞稿50字
2014/02/04 职场文书
艺术学院毕业生自我评价
2014/03/02 职场文书
2015年销售内勤工作总结
2015/04/27 职场文书
2015年小学远程教育工作总结
2015/07/28 职场文书
导游词之镇江焦山
2019/11/21 职场文书
用python自动生成日历
2021/04/24 Python
完美解决golang go get私有仓库的问题
2021/05/05 Golang
golang中的struct操作
2021/11/11 Golang
关于对TypeScript泛型参数的默认值理解
2022/07/15 Javascript