Django Form组件

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>,校验成功返回对应的值,校验失败主动抛出异常
- 局部钩子用来校验`某个字段`是否合法,着眼于局部;全局钩子用来校验`多个字段之间`是否满足某种关系(合法性),着眼于全局
- 局部钩子在前,全局钩子在后
原文地址:https://www.cnblogs.com/zzliu/p/10595140.html