第十四章 form组件
1. form简介
1. Form.is_bound
-
-
通过is_bound(BaseForm类变量)可以查看是否绑定数据
-
一旦创建了一个form实例,不管是否有数据,实例变量都应该视为不可变类型。
2. using forms to validate data
-
没有数据的form通不过校验,错误字典为空
2.1 Form.clean()
-
分别为字段添加自定义validation,就会执行clean方法
2.2 Form.is_valid()
-
通过Form校验数据的有效性,会调用is_valid()方法校验数据并返回True 或 False
2.3 Form.errors
-
通过@property装饰
-
errors,字典的keys是字段名,字典values是错误列表(unicode 字符串)
-
字段的错误信息存储在list中
-
校验只会执行一次,
2.4 Form.errors.as_data()
-
Form.errors.as_json(), 序列化错误字典
2.5 Form.add_error(field, error)
-
如果字段的values是None,会通过Form.non_field_errors()返回一个non—field 错误,可以是一个简单的字符串或者ValidationError的实例。
-
Form.add_error():当调用这个方法时,会自动移除字段的clean_data。
2.6 Form.has_error(field, code=None)
-
返回以个bool值,表示数据是否合法。
2.7 Form.non_field_errors()
-
从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类的对象生成的 -->生成HTML标签功能
-
当用户名和密码输入为空或输错之后 页面都会提示 -->用户提交校验功能
-
-
{# 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;
-
常用字段
-
CharField
-
ChoiceField
-
MultipleChoiceField
-
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='密码')
from django import forms class RegForm(forms.Form): pwd = forms.CharField( min_length=6, label='密码', # 表示输入密码时,为密文显示 widget = forms.widgets.PasswordInput, )
3.4 radioSelect
-
单radio值为字符串,单选点击框
-
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(), )
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(), )
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的注意事项
-
在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 获取的值无法实时更新,那么需要自定义构造方法从而达到此目的。
3.9 动态数据
# 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})
- 方式一
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'))
- 方式二
from django import forms # 从数据库中直接读取 hobby = forms.ModelChoiceField(queryset=models.Hobby.objects.all())
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}$', '手机号不合法')]
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
-
通过校验规则 必须返回当前所有字段的值
-
不通过校验规则 抛出异常 '__
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
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' })
-
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.fields:OrderedDict
-
self.clean_data: {key:value, key: value...}
-
diisabled:默认是False
-
执行full_clean方法
-
定义错误字典
-
存放清洗过数据的字典
-
-
执行self.clean_fields方法
-
循环所有字段,获取当前字段值,
-
对值校验(内置和自定义校验)
-
通过校验self.clean_data[name] = value
-
如果有局部钩子,就要执行校验
-
通过则,self.clean_data[name] = value
-
不通过,self._errors添加当前字段错误,并且删除:del self.clean_data[name]
-
-
没有通过self._errors添加当前字段错误
-
-
执行全局钩子clean方法
-