Loading

form组件

【一】FROM介绍

【1】引入

  • 现有一需求,在不使用ajax的前提下,使用form组件完成向后端发送数据,后端获取到用户名和密码,并且判断用户名和密码是否符合一定条件
  • 如密码长度限制,以及用户名查重等等,如果不符合条件,就需要将相关的消息渲染到前端页面
  • form组件就可以很好的实现以上需求

【2】功能介绍

  • 生成页面可用的HTML标签
  • 对用户提交的数据进行校验
  • 保留上次输入内容

【二】form表单实现需求

【1】思路

  • 在书写前端页面的时候可以在输入框后面加一个span标签
  • 后端函数内可以定义一个字典,在里面放一些反馈信息
  • 后端拿到数据后做一些校验,并且将反馈信息加入到字典内
  • 通过render函数将这个字典传到前端页面,在前端页面用模板语法渲染对应的返回消息

【2】代码

前端页面

<body>
<form action="" method="post">
        <p>username:<input type="text" class="" name="username"><span style="color: red">{{ info_dict.username }}</span></p>
        <p>password:<input type="text" class="" name="password"><span style="color: red">{{ info_dict.password }}</span></p>
        <p><button type="submit" class="btn btn-info"> 登录</button></p>
</form>
</body>

后端逻辑

def ab_form(request):
    '''
    无论是post请求还是get请求,页面都能获取到这个字典,
    get请求一定没有值,
    post请求可能有值
    '''
    info_dict = {'username': '', 'password': ''}
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username != 'green':
            info_dict['username'] = '用户名错误'
        if password != '123123':
            info_dict['password'] = '密码错误'
    return render(request, 'ab_form.html', locals())

【3】为何不在前端校验数据

  • 仔细想想可以发现,对于数据的校验也可以在前端用js完成,为什么要那么麻烦的想办法发到前端再进行校验呢
  • 这是因为前端的校验存在的风险较大,可能会被别人通过爬虫程序直接绕过前端页面向后端提交数据
  • 所以数据的校验在前端可有可无,而后端必须要有。

【三】Forms组件的使用

  • Forms组件可以代替我们后端手动校验数据的过程
  • 通过导入Form这个类实例化出来对象,再使用封装好的方法就可以快速校验数据

【1】实例化对象

from django import forms


class MyForm(forms.Form):
    # username 最大八位最小三位
    username = forms.CharField(max_length=10, min_length=3)
    # password 最大八位最小三位
    password = forms.CharField(max_length=10, min_length=3)
    # 必须符合邮箱格式 xxx@xx.com
    email = forms.EmailField()
    
# 实例化对象,传入获取的数据
form_obj = MyForm({'username': 'green',
                   'password': '12',
                   'email': '123123@qq.com'
                   })

【2】常用方法

# 数据是否完全合法(is_valid)
form_obj.is_valid()

# 查看合法数据(changed_data) 展示符合校验规则的数据
form_obj.changed_data      # ['username', 'password']

# 查看不合法的数据(errors)
form_obj.errors

【3】多传不影响,少传有影响

多传参数是否会报错?

form_obj = MyForm({'username': 'green',
                   'password': '123',
                   'email': '123123@qq.com',
                   'hobby': 'music'
                   })

res = form_obj.is_valid()
print(res) # True

# 多传不会报错

少传参数是否报错

form_obj = MyForm({'username': 'green',
                   'password': '123'
                   })

res = form_obj.is_valid()
print(res) # False

# 少传会报错

【四】forms组件渲染HTML页面

  • 自己手动写html代码有时略显麻烦,form组件可以为我们快速渲染一个输入标签的页面如(input/select),但是它不会自动渲染提交按钮
  • 首先需要在后端视图函数里面实例化一个forms对象,不需要填参数
from django import forms


class MyForm(forms.Form):
    # username 最大八位最小三位
    username = forms.CharField(max_length=10, min_length=3, label='用户名')
    # password 最大八位最小三位
    password = forms.CharField(max_length=10, min_length=3, label='密码')
    # 必须符合邮箱格式 xxx@xx.com
    email = forms.EmailField(label='邮箱')


def ab_form(request):
    form_obj = MyForm() # 实例化一个空对象

return render(request, 'ab_form.html', locals())

【1】渲染方式一

  • 通过模板语法拿到后端视图函数传来的空对象,这个对象可以用as_p,as_ul,as_table等方法直接渲染出来输入框页面
  • 封装程度很高,不利于自定义拓展,一般用于本地测试
<h1>封装程度高,不利于后续扩展,但是书写方便,一般用于本地测试</h1>
<h1>第一种渲染方式 as_p</h1>
{{ form_obj.as_p }}

【2】渲染方式二

  • 通过空对象点字段名方法(form_obj.username)可以渲染出来该字段对应的输入框
  • 通过空对象点字段名点lable(form_obj.username.lable)可以渲染出来对应字段内地lable属性,如果没有该属性会自动渲染该字段名的大写开头形式
  • 该方法封装程度低,利于拓展,但是需要书写的代码太多,十分笨重
<p>{{ form_obj.username.label }}{{ form_obj.username }}</p>
<p>{{ form_obj.password.label }}{{ form_obj.password }}</p>
<p>{{ form_obj.email.label }}{{ form_obj.email }}</p>

【3】渲染方式三

  • 可以通过for循环form_obj对象,每一次循环都会拿到等价于form_obj.username的东西
  • 该方法不仅书写的代码量少,还有便于拓展,十分推荐使用
{% for form in form_obj %}
    <p>{{ form.label }}:{{ form }}</p>
{% endfor %}

【五】forms组件渲染错误信息

  • 在没有使用forms组件之前,都是通过在后端提前定义一个信息字典,在前端的每一个输入框之后都加一个span标签
  • 后端拿到前端发来的数据,并且进行校验,如果错误就把对应的信息加到字典里,这样一来前端在提交数据之后就能在span标签内渲染对应的信息了
  • 可是这样十分麻烦,用来forms组件就变得十分便捷

【1】forms组件

后端视图函数

def ab_form(request):
	form_obj = MyForm()
    if request.method == 'POST':
        # 如果是post请求 就将这个post请求的内容赋值给这个空对象
        # 这样form组件内部就能自动校验数据是否合法
        form_obj = MyForm(request.POST)
        if form_obj.is_valid():
            return HttpResponse('OK')

    return render(request, 'ab_form.html', locals())

后端form类

class MyForm(forms.Form):
    # username 最大八位最小三位
    # error_messages通过这个参数可以自定义对应的错误消息
    username = forms.CharField(max_length=10, min_length=3, label='用户名',
                               error_messages={
                                   'max_length': '用户名最长为10位',
                                   'min_length': '用户名最短为3位',
                                   'required': '用户名不能为空'

                               })
    # password 最大八位最小三位
    password = forms.CharField(max_length=10, min_length=3, label='密码',
                               error_messages={
                                   'max_length': '密码最长为10位',
                                   'min_length': '密码最短为3位',
                                   'required': '密码不能为空'
                               })
    # 必须符合邮箱格式 xxx@xx.com
    email = forms.EmailField(label='邮箱',
                             error_messages={
                                 'invalid': '邮箱格式不正确',
                                 'required': '邮箱不能为空'
                             })

前端页面

  • 这里form标签的novalidate属性是为了取消浏览器的数据校验
  • form.errors.0可以拿到对应字段动态渲染的错误消息
<form action="" novalidate method="post">
    {% for form in form_obj %}
        <p>
            {{ form.label }}:{{ form }}
            <span style="color: red;">{{ form.errors.0 }}</span>
        </p>
    {% endfor %}
    <input type="submit" class="btn btn-info">

</form>

【六】钩子函数

  • 钩子函数是用于对数据二次校验的,校验的内容可以有我们自己高度的自定义,
  • 钩子函数是定义在form类里面的
  • 钩子函数只有在第一层校验正确的情况下才会被调用
  • 钩子 函数分为局部钩子和全局钩子,局部钩子适用于给某一个字段的数据进行校验,全局钩子适用于多个字段的校验

form类代码

class MyForm(forms.Form):
    # username 最大八位最小三位
    # error_messages通过这个参数可以自定义对应的错误消息
    username = forms.CharField(max_length=10, min_length=3, label='用户名',
                               error_messages={
                                   'max_length': '用户名最长为10位',
                                   'min_length': '用户名最短为3位',
                                   'required': '用户名不能为空'

                               })
    # password 最大八位最小三位
    password = forms.CharField(max_length=10, min_length=3, label='密码',
                               error_messages={
                                   'max_length': '密码最长为10位',
                                   'min_length': '密码最短为3位',
                                   'required': '密码不能为空'
                               })

    password_confirm = forms.CharField(max_length=10, min_length=3, label='密码',
                                       error_messages={
                                           'max_length': '密码最长为10位',
                                           'min_length': '密码最短为3位',
                                           'required': '密码不能为空'
                                       })
    # 必须符合邮箱格式 xxx@xx.com
    email = forms.EmailField(label='邮箱',
                             error_messages={
                                 'invalid': '邮箱格式不正确',
                                 'required': '邮箱不能为空'
                             })

【1】局部钩子

  • 此时我想对用户名进行进一步限制,比如用户名不能含有666,
# 局部钩子需要重写clean_字段名 函数
def clean_username(self):
    # 获取到用户名,因为到了这一步说明第一步校验是通过的,所以直接从正确的数据里面拿
    username = self.cleaned_data.get('username')
    # 进行自定义校验
    if '666' in username:
        # 通过add_error 添加反馈信息, 这个方法有两个参数,第一个参数是对应的字段,第二个参数是填入的错误消息
        self.add_error('username','不能扣666')
    # 最后需要将钩出来的数据返回回去
    return username

【2】全局钩子

  • 此时我加一个密码二次确认的过程
# 全局钩子就需要重写clean函数
def clean(self):
    # 获取到对应的数据
    password = self.clean_data.get('password')
    password_confirm = self.cleaned_data.get('password_confirm')
    # 写自定义校验逻辑
    if password != password_confirm:
        # 添加错误消息
        self.add_error('password_confirm','两次密码不一致')
    # 因为是全局钩子,就需要把所有的数据都返回回去
    return self.clean_data

【七】forms组件其他参数以及补充知识点

label 字段名
error_messages 自定义错误信息
initial 默认值
required 控制字段是否必填 ,不是必填 required=False


# 给input框定义type样式
# 参数是一个字典,字典的键是属性名,值是属性值
# 样式如果要加多个,用空格隔开即可
widget=forms.widgets.TextInput(attrs={'class':'form-control c1 c2'})

# 正则表达式
# 这是一段手机号码的正则表达式,使用前需要先导入模块
# 可以一次写多个正则
from django.core.validators import RegexValidator
phone = forms.CharField(
    validators=[
        RegexValidator(regex=r'^\+?1?\d{9,15}$', message="手机号格式不正确"),
    ]
)

【1】常用字段

# 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):
        error_messages={
            "required": "不能为空",
            "invalid": "格式错误",
            "min_length": "用户名最短8位"
        }
    )
    
# radioSelect 单radio值为字符串
class LoginForm(forms.Form):
    gender = forms.fields.ChoiceField(
        choices=((1, "男"), (2, "女"), (3, "保密")),
        label="性别",
        initial=3,
        widget=forms.widgets.RadioSelect()
    )
    
# sekect 单选
class LoginForm(forms.Form):
    ...
    hobby = forms.ChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
        label="爱好",
        initial=3,
        widget=forms.widgets.Select()
    )
    
# 

posted @ 2024-03-25 17:31  HuangQiaoqi  阅读(7)  评论(0编辑  收藏  举报