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在校内发人人网状态(人人网看状态)
Feb 19 Python
python利用有道翻译实现&quot;语言翻译器&quot;的功能实例
Nov 14 Python
python解析json串与正则匹配对比方法
Dec 20 Python
numpy.where() 用法详解
May 27 Python
python替换字符串中的子串图文步骤
Jun 19 Python
django项目简单调取百度翻译接口的方法
Aug 06 Python
python实现的按要求生成手机号功能示例
Oct 08 Python
python实现上传文件到linux指定目录的方法
Jan 03 Python
python第三方库学习笔记
Feb 07 Python
Python中logging日志库实例详解
Feb 19 Python
详解python中GPU版本的opencv常用方法介绍
Jul 24 Python
python操作链表的示例代码
Sep 27 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中在foreach中使用foreach ($arr as &amp;$value) 这种类型的解释
2013/06/24 PHP
php替换字符串中间字符为省略号的方法
2015/05/04 PHP
ThinkPHP使用Ueditor的方法详解
2016/05/20 PHP
php实现生成code128条形码的方法详解
2017/07/19 PHP
最好用的省市二级联动 原生js实现你值得拥有
2013/09/22 Javascript
jquery简单的弹出层浮动层代码
2015/04/27 Javascript
javascript实现自动填写表单实例简析
2015/12/02 Javascript
CascadeView级联组件实现思路详解(分离思想和单链表)
2016/04/12 Javascript
原生JavaScript编写canvas版的连连看游戏
2016/05/29 Javascript
jQuery简单实现彩色云标签效果示例
2016/08/01 Javascript
js从外部获取图片的实现方法
2016/08/05 Javascript
JavaScript中如何使用cookie实现记住密码功能及cookie相关函数介绍
2016/11/10 Javascript
详解JS数组Reduce()方法详解及高级技巧
2017/08/18 Javascript
微信小程序自动客服功能
2017/11/02 Javascript
使用vue-cli打包过程中的步骤以及问题的解决
2018/05/08 Javascript
微信小程序实现下拉菜单切换效果
2020/03/30 Javascript
Vue 之孙组件向爷组件通信的实现
2019/04/23 Javascript
Vue注册组件命名时不能用大写的原因浅析
2019/04/25 Javascript
Vue中import from的来源及省略后缀与加载文件夹问题
2020/02/09 Javascript
python使用循环实现批量创建文件夹示例
2014/03/25 Python
Python的动态重新封装的教程
2015/04/11 Python
python中abs&amp;map&amp;reduce简介
2018/02/20 Python
python实现多线程网页下载器
2018/04/15 Python
对django2.0 关联表的必填on_delete参数的含义解析
2019/08/09 Python
Python迭代器iterator生成器generator使用解析
2019/10/24 Python
HTML5安全介绍之内容安全策略(CSP)简介
2012/07/10 HTML / CSS
法拉利英国精品店:Ferraris Boutique UK
2019/07/20 全球购物
Hotels.com泰国:酒店预订网站
2019/11/20 全球购物
两年的个人工作自我评价
2014/01/10 职场文书
服装设计专业求职信
2014/06/16 职场文书
学习优秀党务工作者先进事迹材料思想报告
2014/09/17 职场文书
小学优秀班主任材料
2014/12/17 职场文书
2015年超市收银员工作总结
2015/04/25 职场文书
在K8s上部署Redis集群的方法步骤
2021/04/27 Redis
Win11控制面板快捷键是什么?Win11打开控制面板的方法汇总
2022/07/07 数码科技
苹果macOS 13开发者预览版Beta 8发布 正式版10月发布
2022/09/23 数码科技