Form组件的简单使用
创建models类
以员工注册为例,创建一个表示员工表的类
models.py
from django.db import models
# Create your models here.
class Emp(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
salary = models.DecimalField(max_digits=8, decimal_places=2)
## 自定义Form类 在app1下面新建一个py文件 myForm.py(myForm是自己起的名字)
from django import forms
class EmpForm(forms.Form):
# 这里定义的字段和models里面的Emp表的字段必须一一对应
# label的值为input对应的label标签显示的内容
# error_message:验证未通过时显示的信息
name = forms.CharField(min_length=5, label='姓名', error_messages={
# 键:字段约束,required是form组件自动加的
# 值:验证未通过时显示的错误信息
'required': '姓名不能为空',
'min_length': '姓名至少为5位',
}) # 定制约束条件
age = forms.IntegerField(label='年龄')
salary = forms.DecimalField(max_digits=5, decimal_places=2, label='工资')
## 在视图函数里生成form对象并将form对象传给前端 views.py ```python from django.shortcuts import render, HttpResponse, redirect from app1.myForm import EmpForm from app1 import models # Create your views here.
1. 自己写html页面
def login(request):
if request.method == 'POST':
pass
else:
return render(request, 'login.html')
2. 用form自动生成html页面
def login(request):
if request.method == 'POST':
# 把request.POST作为参数传进去
form = EmpForm(request.POST)
# 验证数据
if form.is_valid():
# 打印验证过的数据(可以写进数据库里)
print('form.cleaned_data:', form.cleaned_data)
models.Emp.objects.create(**form.cleaned_data)
else:
# 打印错误信息
print('form.errors:', form.errors)
return render(request, 'login.html', {'form': form})
else:
form = EmpForm() # 实例化一个EmpForm对象
# 把form传给前端页面
return render(request, 'login.html', {'form': form})
<br>
## 在前端页面里用form对象生成html标签
login.html
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>员工注册</h1>
{# 1. 自己写的html页面 #}
{#<form action="" method="post">#}
{# {% csrf_token %}#}
{# <p>姓名:<input type="text" name="name"></p>#}
{# <p>年龄:<input type="text" name="age"></p>#}
{# <p>工资:<input type="text" name="salary"></p>#}
{# <input type="submit">#}
{#</form>#}
{# 2. aas_p: 一次展示所有字段 #}
{#<form action="" method="post">#}
{# Return this form rendered as HTML <p>s. #}
{# 注意会把intfield渲染成type=number #}
{# {{ form.as_p }}#}
{# 注意这里不会渲染submit标签,需要自己写 #}
{# <input type="submit">#}
{#</form>#}
{# 3. 手动获取form对象的字段 #}
{#<form action="" method="post">#}
{# <div>#}
{# form.name是一个form表单下输入姓名的的标签 #}
{# form.name.name是这个标签的值 #}
{# <label for="id_{{ form.name.name }}">姓名</label>#}
{# form组件会自动给input标签加上一个id,id名为id_字段名 #}
{# {{ form.name }} <span>{{ form.name.errors.0 }}</span>#}
{# </div>#}
{# <div>#}
{# <label for="id_{{ form.age.name }}">年龄</label>#}
{# {{ form.age }} <span>{{ form.age.errors.0 }}</span>#}
{# </div>#}
{# <div>#}
{# <label for="id_{{ form.salary.name }}">工资</label>#}
{# {{ form.salary }} <span>{{ form.salary.errors.0 }}</span>#}
{# </div>#}
{# <input type="submit">#}
{#</form>#}
{# 4. for循环获取form对象的字段 #}
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div>
<label for="id_{{ field.name }}">{{ field.label }}</label>
{# field.errors是一个列表,一般只显示第一个就可以了,解决了再显示下一个 #}
{{ field }} <span>{{ field.errors.0 }}</span>
</div>
{% endfor %}
<input type="submit">
</form>
{# 注意:用这种方式生成的input标签会自动加上required属性,也就是不能为空 #}
{# 为空时提交不了,浏览器会显示必须填写此字段 #}
{# 如果要去掉这个,在form里加上novalidate,告诉浏览器不用验证直接提交(正常期间狂不用加) #}
{# 提交验证未通过时,form组件会保留用户输入的信息 #}
</body>
</html>
## 小结 ### 什么是Form组件? Form组件就是用一个类来表示form表单,类的属性对应form表单里面的input标签,注意不包括submit按钮 ### Form组件有哪些作用? - 页面初始化,生成HTML标签 - 校验用户数据(显示错误信息) - HTML Form提交保留上次提交数据
# 局部钩子和全局钩子 ## 局部钩子 ### 什么是局部钩子? 在自定义Form类下面定义一个函数,名字叫:clean_字段名字,内部,取出该字段,进行校验,如果通过,将该字段返回,如果失败,抛出异常(ValidationError) ### 定义局部钩子
from django import forms
from django.core.exceptions import ValidationError
from app01 import models
class EmpForm(forms.Form):
name = forms.CharField(min_length=5, label="姓名", error_messages={
"required": "该字段不能为空!",
"min_length": "用户名太短。"}
)
age = forms.IntegerField(label="年龄")
salary = forms.DecimalField(max_digits=5, decimal_places=2, label="工资")
# 注意这里函数的名字必须是clean_字段名的形式
def clean_name(self): # 局部钩子
val = self.cleaned_data.get("name")
# 如果是数字
if val.isdigit():
# 抛出异常,使用ValidationError之前要导入
raise ValidationError("用户名不能是纯数字")
elif models.Emp.objects.filter(name=val):
raise ValidationError("用户名已存在!")
else:
return val
姓名输入纯数字时,显示错误信息 ![](https://img2018.cnblogs.com/blog/1542801/201903/1542801-20190325193924696-1750874306.png)
输入数据库已存在的姓名时,显示错误信息 ![](https://img2018.cnblogs.com/blog/1542801/201903/1542801-20190325193831756-1465307449.png)
输入正确的信息时,正常提交
检查数据库是否写入
## 全局钩子 ### 什么是全局钩子? 局部钩子只是校验一个字段是否合法,如果要校验不同字段之间的值,需要使用全局钩子,也就是说,全局钩子是校验不同字段之间的一种方法
###定义全局钩子 在EmpForm下面添加一个字段r_salary ```python class EmpForm(forms.Form): # 这里定义的字段和models里面的Emp表的字段必须一一对应 # label的值为input对应的label标签显示的内容 # error_message:验证未通过时显示的信息 name = forms.CharField(min_length=5, label='姓名', error_messages={ # 这里每一个键值对表示对应的字段验证未通过时显示的错误信息 'required': '姓名不同为空', 'min_length': '姓名太短', }) # 定制约束条件 age = forms.IntegerField(label='年龄') salary = forms.DecimalField(max_digits=5, decimal_places=2, label='工资') r_salary = forms.DecimalField(max_digits=5, decimal_places=2, label="请再次输入工资")
<br>
定义clean方法
```python
from django import forms
from app1 import models
from django.core.exceptions import ValidationError
class EmpForm(forms.Form):
# 这里定义的字段和models里面的Emp表的字段必须一一对应
# label的值为input对应的label标签显示的内容
# error_message:验证未通过时显示的信息
name = forms.CharField(min_length=5, label='姓名', error_messages={
# 这里每一个键值对表示对应的字段验证未通过时显示的错误信息
'required': '姓名不同为空',
'min_length': '姓名太短',
}) # 定制约束条件
age = forms.IntegerField(label='年龄')
salary = forms.DecimalField(max_digits=5, decimal_places=2, label='工资')
r_salary = forms.DecimalField(max_digits=5, decimal_places=2, label="请再次输入工资")
def clean_name(self):
val = self.cleaned_data.get('name')
if val.isdigit():
raise ValidationError('姓名不能不能全是数字')
elif models.Emp.objects.filter(name=val):
raise ValidationError('用户名已存在')
else:
return val
def clean(self):
salary = self.cleaned_data.get('salary')
r_salary = self.cleaned_data.get('r_salary')
if salary != r_salary:
raise ValidationError('工资输入有误')
else:
return self.cleaned_data
修改视图函数 ```python from django.shortcuts import render, HttpResponse, redirect from app1.myForm import EmpForm from app1 import models
def login(request):
if request.method == 'POST':
# 把request.POST作为参数传进去
form = EmpForm(request.POST)
# 验证数据
if form.is_valid():
# 打印验证过的数据(可以写进数据库里)
data = form.cleaned_data
# 注意一定要删除多余的数据
data.pop('r_salary')
models.Emp.objects.create(**data)
return Httpresponse('添加成功')
else:
# 获取全局错误信息
clear_errors = form.errors.get('all')
return render(request, 'login.html', {'form': form, 'clear_errors': clear_errors})
else:
form = EmpForm() # 实例化一个EmpForm对象
# 把form传给前端页面
return render(request, 'login.html', {'form': form})
<br>
两次工资输入不一致时
![](https://img2018.cnblogs.com/blog/1542801/201903/1542801-20190325203013010-1350064280.png)
<br>
## 小结
- 局部钩子和全局钩子都是用来<font color='#ff0000'>校验用户输入的数据</font>,校验成功返回对应的值,校验失败主动抛出异常
- 局部钩子用来校验`某个字段`是否合法,着眼于局部;全局钩子用来校验`多个字段之间`是否满足某种关系(合法性),着眼于全局
- 局部钩子在前,全局钩子在后