第十四章 form组件

1. form简介

1. Form.is_bound
  1. form对象可以绑定或者不绑定数据

  2. 通过is_bound(BaseForm类变量)可以查看是否绑定数据

  3. 一旦创建了一个form实例,不管是否有数据,实例变量都应该视为不可变类型

2. using forms to validate data
  • 没有数据的form通不过校验,错误字典为空

2.1 Form.clean()
  • 分别为字段添加自定义validation,就会执行clean方法

2.2 Form.is_valid()
  • 通过Form校验数据的有效性,会调用is_valid()方法校验数据并返回TrueFalse

2.3 Form.errors
  1. 通过@property装饰

  2. errors,字典的keys是字段名,字典values是错误列表(unicode 字符串)

  3. 字段的错误信息存储在list中

  4. 校验只会执行一次,

2.4 Form.errors.as_data()
  • Form.errors.as_json(), 序列化错误字典

2.5 Form.add_error(field, error)
  1. 如果字段的values是None,会通过Form.non_field_errors()返回一个non—field 错误,可以是一个简单的字符串或者ValidationError的实例。

  2. Form.add_error():当调用这个方法时,会自动移除字段的clean_data。

2.6 Form.has_error(field, code=None)
  1. 返回以个bool值,表示数据是否合法。

2.7 Form.non_field_errors()
  1. 从Form.errors返回一个包含Form.clean()方法抛出的ValidationErrors和使用Form.add_error(None, '…')的错误list

2.form简单应用

1.1 普通手写功能
# views.py
def register(request):
    msg = ''
    if request.method == 'POST':
        username = request.POST.get('username')
        print(username, len(username))
        if len(username) < 6:
            msg = '用户名长度至少6位'
        else:
            # 把用户名和密码写入数据库
            return HttpResponse('注册成功')
    return render(request, 'register.html', {'msg': msg})
{# register.html #}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Register</title>
</head>
<body>
<form action='/register/' method='post'>
    {% csrf_token %}
    <p>
        <input type='text' name='username' placeholder="用户名" autofocus>{{ msg }}
    </p>
    <p>
        <input type='password' name='pwd' placeholder="密码">
    </p>
    <p>
        <button>提交</button>
    </p>
</form>
</body>
</html>
1.2 使用form组件实现
# views.py
from django import forms

class RegForm(forms.Form):
    username = forms.CharField()
    pwd = forms.CharField()

def register(request):
    obj = RegForm()
    if request.method == 'POST':
        obj = RegForm(request.POST)
        if obj.is_valid():
            return HttpResponse('注册成功')
    return render(request, 'register.html', {'obj': obj})
{# register.html #}
{# form表单中的novalidate属性表示浏览器不进行校验 #}
<form action="/register/" method="post" novalidate>
    {% csrf_token %}
    {{ obj.as_p }}  {# 使用默认方式生成input和label标签 #}
        
      {# 指定label的值 #}
    <p>
        <label for="{{ obj.username.id_for_label }}">用户名:</label>
        {{ obj.username }} {{ obj.username.errors.0 }}
    </p>
    <p>
        <label for="{{ obj.pwd.id_for_label }}">密码:</label>
        {{ obj.pwd }} {{ obj.pwd.errors.0 }}
    </p>
      {{ obj.errors }}
    <button>提交</button>
    </p>
</form>
  • form 表单的功能
    • 前端页面是form类的对象生成的 -->生成HTML标签功能

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

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

{# django模版 #}
{{ form_obj.as_p }} {# 产生一个个p标签和label、input标签 #}
{{ obj.username }}  {# 用户名字段内容 #}
{{ obj.字段名.id_for_label }}  {# 生成label中的,字段内容(字段id) #}
{{ obj.errors }}      {# 错误的所有内容 #}
{{ obj.errors.0 }}  {# 错误的所有内容中的第一个值,dict #}
{{ obj.username.errors }}    {# 该字段的错误信息 #}
{{ obj.username.errors.0 }}  {# 该字段的错误信息的第一个值 #}
# views.py

# 字段参数(form类属性对象的参数)
1. required=True,                # 是否允许为空
2. widget=None,                  # HTML插件
3. label=None,                   # 用于生成Label标签或显示内容
4. initial=None,                 # 初始值
5. error_messages=None,         # 错误信息 {'required': '不能为空', 'invalid': '格式错误'}
6. validators=[],                # 自定义验证规则
7. disabled=False,               # 是否可以编辑

min_length=6,                                  # 最小长度
max_length=8,                                    # 最大长度,前端页面输入到8位之后,不能继续输入

3. form组件字段与插件

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

  • 常用字段

    1. CharField

    2. ChoiceField

    3. MultipleChoiceField

    4. ModelChoiceField

3.1 initial
  • 初始值,input框中的默认值

from django import forms
class RegForm(forms.Form):
   username = forms.CharField(
     min_length=6,
     # 给username字段设置默认值
     label = '用户名',
     initial = 'henry',
   )
    pwd = forms.CharField(min_length=6, label='密码')
3.2 error_messages
  • 重写错误信息

from django import forms
class RegForm(forms.Form):
   username = forms.CharField(
     min_length=6,
     # 给username字段设置默认值
     label = '用户名',
     initial = 'henry',
     error_messages = {
       'required': '不能为空',
       'invalid': '格式有误',
       'min_length': '用户名最短6位'
     }
   )
    pwd = forms.CharField(min_length=6, label='密码')
3.3 password
from django import forms
class RegForm(forms.Form):
    pwd = forms.CharField(
      min_length=6, 
      label='密码',
      # 表示输入密码时,为密文显示
      widget = forms.widgets.PasswordInput,
    )
3.4 radioSelect
  • 单radio值为字符串,单选点击框

  • 生成ul标签

from django import forms
class RegForm(forms.Form):
   username = forms.CharField(
     min_length=6,
     # 给username字段设置默认值
     label = '用户名',
     initial = 'henry',
     error_messages = {
       'required': '不能为空',
       'invalid': '格式有误',
       'min_length': '用户名最短6位'
     }
   )
  pwd = forms.CharField(min_length=6, label='密码',)
    gender = forms.fields.ChoiceField(
      choices=((0, 'female'), (1, 'male'), (3, 'secret')),
    label = '性别',
    initial = 3,
    widget = forms.widgets.RadioSelect()
  )
3.5 单选select
  • 单选下拉框

from django import forms
class RegForm(forms.Form):
  ...
  hobby = forms.ChoiceField(
      choices = ((1, 'travelling'), (2, 'reading'), (3, 'listening'),),
    label = '爱好',
    initial = 3,
    widget=forms.widgets.Select(),
  )
3.6 多选select
from django import forms
class RegForm(forms.Form):
  ...
  hobby = forms.MultipleChoiceField(
          choices=(('1', 'travelling'), ('2', 'reading'), ('3', 'listening'),),
          label='爱好',
          initial=['3'],
          widget=forms.widgets.SelectMultiple(),
      )
3.7 单选checkbox
from django import forms
class RegForm(forms.Form):
  ...
  keep = forms.ChoiceField(
      label = '是否记住密码',
    initial = 'checked',
    widget=forms.widgets.CheckboxInput(),
      )
3.8 多选checkbox
from django import forms
class RegForm(forms.Form):
  ...
   hobby = forms.fields.MultipleChoiceField(
       choices=((1, 'travelling'), (2, 'reading'), (3, 'listening'),),
        label="爱好",
        initial=[1, 3],
        widget=forms.widgets.CheckboxSelectMultiple()
    )

关于choice的注意事项

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

3.9 动态数据
  • views函数

# views.py
def register(request):
    obj = RegForm()
    if request.method == 'POST':
        obj = RegForm(data=request.POST)
        if obj.is_valid():
            return HttpResponse('注册成功')
    return render(request, 'register.html', {'obj': obj})
  • 方式一:使用MutipleChoiceField
from django import forms
class RegForm(forms.Form):
      def __init__(self, *args, **kwargs):
            super(RegForm, self).__init__( *args, **kwargs)
            self.fields['hobby'].choices = models.Hobby.objects.values_list('pk', 'name')
    # 从数据库中读取
      hobby = forms.MutipleChoiceField(choices=models.Hobby.objects.all().values_list('pk', 'name'))
  • 方式二:使用ModelChoiceField
from django import forms
# 从数据库中直接读取
hobby = forms.ModelChoiceField(queryset=models.Hobby.objects.all())

4. 校验

4.1 内置校验
from django.forms improt Form
from django.core.validators import RegexValidator

class MyForm(From):
  phone = forms.CharField(
    # 正则校验器中,第二个参数是提示信息
    validators=[RegexValidator(r'1[3-9]\d{9}$', '手机号不合法')]
4.2 自定义校验
from django.core.exceptions import ValidationError
def checkname(value):
  if 'o' in value:
    rasie ValidationError('用户名不合法')
    
class RegForm(forms.Form):
   username = forms.CharField(
     min_length=6,
     # 给username字段设置默认值
     label = '用户名',
     initial = 'henry',
     validators = [checkname,...]
    )
    pwd = forms.CharField(
        widget = forms.widgets.PasswordInput(),
    )
4.3 钩子
  • 局部钩子

    • 通过校验规则 必须返回当前字段的值

    • 不通过校验规则 抛出异常

class RegForm(forms.Form):  
  username = forms.CharField(label='用户名')
  def clean_username(self):
    v = self.cleaned_data.get('username')
    if 'o' in v:
      raise ValidationError('用户名不合法。。。。。')
    return v

全局钩子

  • 通过校验规则 必须返回当前所有字段的值

  • 不通过校验规则 抛出异常 '__all__'

class RegForm(forms.Form):  
    pwd = forms.CharField(
          label='密码',
          widget=forms.widgets.PasswordInput,)
    re_pwd = forms.CharField(
          label='密码',
          widget=forms.widgets.PasswordInput,)
    def clean(self):
          super().clean() / self._validate_unique = True
        if not self.cleaned_data.get('pwd') == self.cleaned_data.get('re_pwd'):
            self.add_error('re_pwd','两次密码不一致')
            raise ValidationError('两次密码不一致')
          return self.cleaned_data
4.4 批量添加样式
class LoginForm(forms.Form):
    username = forms.CharField(
        min_length=5,
        label="用户名",
        initial="henry",
        error_messages={
            "required": "不能为空",
            "invalid": "格式错误",
            "min_length": "用户名最短5位"
        }
    ...

    def __init__(self, *args, **kwargs):
        super(LoginForm, self).__init__(*args, **kwargs)
        for field in iter(self.fields):
            self.fields[field].widget.attrs.update({
                'class': 'form-control'
            })
4.5 ModelForm
  • forms.widgets.PasswordInput(attrs={"class": "c1"})

  • 可以为指定字段使用,attrs设置属性值为dict类型

class BookForm(forms.ModelForm):

    class Meta:
          # 指定前端生成的标签
        model = models.Book
        # __all__ 表示可以生成所有字段,也可以使用list如['title', 'price'...]
        fields = "__all__"
        # exclude = ['publisher',...],也可以进行排除
        labels = {
            "title": "书名",
            "price": "价格"
        }
        # 插件,用于某个字段的属性修改
        widgets = {
            "password": forms.widgets.PasswordInput(attrs={"class": "c1"}),
        }
model = models.Book  # 对应的Model中的类
fields = "__all__"      # 字段,如果是__all__,就是表示列出所有的字段
exclude = None          # 排除的字段
labels = None           # 提示信息
help_texts = None       # 帮助提示信息
widgets = None          # 自定义插件
error_messages = None   # 自定义错误信息
4.6 is_valid执行流程
  • self.fieldsOrderedDict([('hobby', <django.forms.models.ModelChoiceField object at 0x107c0b080>), …])

  • self.clean_data: {key:value, key: value...}

  • diisabled:默认是False

  1. 执行full_clean方法

    1. 定义错误字典

    2. 存放清洗过数据的字典

  2. 执行self.clean_fields方法

    1. 循环所有字段,获取当前字段值,

    2. 对值校验(内置和自定义校验)

      • 通过校验self.clean_data[name] = value

        1. 如果有局部钩子,就要执行校验

        2. 通过则,self.clean_data[name] = value

        3. 不通过,self._errors添加当前字段错误,并且删除:del self.clean_data[name]

      • 没有通过self._errors添加当前字段错误

    3. 执行全局钩子clean方法

 

posted @ 2020-05-28 21:06  炜琴清  阅读(190)  评论(0编辑  收藏  举报