form与modelform组件

一.Django的forms组件应用场景

1.应用

  • 一般应用于前端的登入、注册界面, 对用户输入的字段进行校验, 快速的判断用户输入的内容是否合法, 并返回信息

2.为何不在前端直接使用JS进行校验

  • 前端的校验可以没有, 但后端的校验必须要有
  • 因为前端的校验弱不禁风, 有很多种方式可以伪装成浏览器发送请求传递数据
  • 或者通过爬虫程序绕过前端页面直接朝后端提交数据

二.Forms组件使用步骤

  • 导入forms组件

from django import forms
  • 定义一个类, 并继承Form,在类中书写要校验的字段, 字段的属性就是要校验的规则

class MyForm(forms.Form):
    username = forms.CharField(min_length=3, max_length=8)  # username字段最少三个字符最大八个字符
    age = forms.IntegerField(min_value=0, max_value=200)  # 年龄最小0 最大200
    email = forms.EmailField()  # 必须符合邮箱格式
  • 实例化得到一个Form对象, 把要校验的数据传入

form_obj = views.MyForm({'username':'jason','age':18,'email':'123'})

forms组件校验方法:

1. form_obj.is_valid()  # 判断数据是否全部符合要求 False(只要有一个不符合结果都是False)    
2. form_obj.cleaned_data  # 获取符合校验条件的数据 {'username': 'jason', 'age': 18}    
3. form_obj.errors  # 获取不符合校验规则的数据及原因 {'email': ['Enter a valid email address.']}

 

三.forms组件基本使用示例

简单的用户注册界面示例

1.常用字段属性

属性 释义
min_length 最小长度
max_length 最大长度
label 该字段的标签
error_messages 错误提示
validators 正则校验器
initial input框里面的初始值
required 是否必填

2.组件文件 form.py

  • 建议在应用文件夹下创建一个专门存放forms组件的文件 (不创建直接写在views.py中也可以)
  • 导入组件并创建类
from django import forms


class RegisterForm(forms.Form):
    # 校验name字段,最大长度为8,最短为3,下面的字段类似
    name = forms.CharField(max_length=8, min_length=3, lable='用户名')
    password = forms.CharField(max_length=10, min_length=4, lable='密码')
    re_password = forms.CharField(max_length=10, min_length=4, lable='确认密码')
    email = forms.EmailField(lable='邮箱')

3.视图层 views.py 文件

  • 实例forms对象, 使用对方法进行校验
from app01.myform import RegisterForm


def register(request):
    if request.method == 'POST':
        # 实例得到form对象,将需要校验的数据传入(数据可以是前端传过来的数据,也可以是自己后端已有的数据)
        # dic = {'name':'alex','password':'1111','re_password':'1111','email':'123@qq.com'}
        # form_data = RegisterForm(dic)  # 示例:直接将已有的数据传入
        form_data = RegisterForm(request.POST)  # 将前端post请求的数据传入

        # 进行校验 is_valid()
        if form_data.is_valid():
            print('校验成功')
            # 获得校验成功的数据 cleaned_data
            print(form_data.cleaned_data)
            return HttpResponse('校验成功')
        else:
            print('检验失败')
            # 获得检验失败的错误提示 errors
            print(form_data.errors)

    # forms组件可以自动生成HTML代码,因此可以直接在页面上进行显示
    form_data = RegisterForm()  # 先产生form空对象,再将其传给前端
    return render(request, 're_form.html', {'form': form_data})

ps : cleaned_data属性必须是经过is_valid( )校验之后才产生的, 在校验之前使用会报错 : 属性不存在

4.路由层 urls.py 文件

path('re_form/', views.register)

5.模板层 re_form.html 文件

<div class="container">
    <div class="row">
        <div class="col-md-4">
            <div class="panel panel-info">
                <div class="panel-heading">注册页面</div>
                <div class="panel-body">
                    <form action="" method="post">
                        <p>用户名:{{ form.name }}</p>
                        <p>密码:{{ form.password }}</p>
                        <p>确认密码:{{ form.re_password }}</p>
                        <p>邮箱:{{ form.email }}</p>
                        <br>
                        <input type="submit" class="btn btn-block btn-success" value="注册">
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

四.forms组件之渲染标签方式

1.自动渲染方式一

  • 就是上面示例中的写法
  • 可扩展性很强, 但是需要书写的代码太多
<form action="" method="post">
    <p>用户名:{{ form.name }}</p>
    <p>密码:{{ form.password }}</p>
    <p>确认密码:{{ form.re_password }}</p>
    <p>邮箱:{{ form.email }}</p>
    <br>
    <input type="submit" class="btn btn-block btn-success" value="注册">
</form>
  • 也可以使用label标签替代前缀(’‘用户名’’、’‘密码’'等等)
<form action="" method="post">
    <p>{{ form.name.label }}:{{ form.name }}</p>
    <p>{{ form.password.label }}:{{ form.password }}</p>
    <p>{{ form.re_password.label }}:{{ form.re_password }}</p>
    <p>{{ form.email.label }}:{{ form.email }}</p>
    <br>
    <input type="submit" class="btn btn-block btn-success" value="注册">
</form>

2.自动渲染方式二(常用方式)

  • 使用for循环
  • 代码书写简单, 并且扩展性也高 (推荐使用)
<form action="" method="post" novalidate>
    {% for foo in form %}
        <div class="form-group">
            {# 循环取出 name,password,re_password,email #}
            <p>{{ foo.label }}:{{ foo }}</p>
        </div>
    {% endfor %}
    <input type="submit" class="btn btn-block btn-success" value="注册">
</form>

3.自动渲染方式三(不常用)

  • 代码书写极少, 封装程度太高, 不便于后续的扩展, 一般情况下只在本地测试使用
<form action="" method="post" novalidate>
    {{ form.as_p }}       {# 与上两种显示方式相同 #}
    {{ form.as_ul }}      {# 前面带点 #}
    {{ form.as_table }}   {# 在一行显示 #}
    <input type="submit" class="btn btn-block btn-success" value="注册">
</form>

 注意事项:

  • forms组件只负责渲染获取用户数据的标签,也就意味着form标签与按钮都需要自己写;
  • 前端的校验是弱不禁风的,最终都需要后端来校验,所以我们在使用forms组件的时候可以直接取消前端帮我们的校验,设置参数 <form action=" "  novalidate>

五.forms组件之渲染错误信息

1.字段参数(举例)

  • required : 是否允许为空,默认False

  • error_messages : 自定义错误信息

  • widget

    : 使用HTML插件

    • TextInput
    • PasswordInput

2.代码演示

  • myform.py 文件
from django import forms
from django.forms import widgets

class RegisterForm(forms.Form):
    # 校验name字段,最大长度为8,最短为3,下面的字段类似
    name = forms.CharField(max_length=8, min_length=3, label='用户名',
                           # 自定义错误信息
                           error_messages={
                               'min_length': '用户名最少3位!',
                               'max_length': '用户账户最大8位',
                               'required': '用户名不能为空!',
                           },
                           # 添加一个form-control类
                           widget=widgets.TextInput(attrs={'class':'form-control'}))
    password = forms.CharField(max_length=10, min_length=4, label='密码',
                               error_messages={
                                   'min_length': '密码最少4位!',
                                   'max_length': '密码最大10位',
                                   'required': '密码不能为空!',
                               },
                               widget=widgets.PasswordInput(attrs={'class':'form-control'}))
    re_password = forms.CharField(max_length=10, min_length=4, label='确认密码',
                                  error_messages={
                                      'min_length': '密码最少4位!',
                                      'max_length': '密码最大10位',
                                      'required': '密码不能为空!',
                                  },
                                  widget=widgets.PasswordInput(attrs={'class':'form-control'}))
    email = forms.EmailField(label='邮箱',
                             error_messages={
                                 'invalid': '邮箱格式不正确',
                                 'required': '邮箱不能为空',
                             },
                             widget=widgets.TextInput(attrs={'class':'form-control'}))
  • views.py 文件
from app01.myform import RegisterForm


def register(request):
    if request.method == 'POST':
        form_data = RegisterForm(request.POST)  # request.POST可以看成是一个字典,直接传给forms类校验

        if form_data.is_valid():
            print(form_data.cleaned_data)
            return HttpResponse('校验成功')
        else:
            # 将带有错误信息的form对象返回前端页面
       
print(form_data.errors)
       return render(request, 're_form.html', {'form': form_data})

    form_data = RegisterForm()  # 将form对象传给前端
    return render(request, 're_form.html', {'form': form_data})
  • re_form.html 文件
<form action="" method="post" novalidate>
    {% for foo in form %}
        {# 在后方设置一个span标签用来放错误提示 #}
        <p>{{ foo.label }}:{{ foo }}
            <span style="color: red">{{ foo.errors.0 }}</span>
        </p>
    {% endfor %}
    <input type="submit" class="btn btn-block btn-success" value="注册">
</form>

ps : <form action="" method="post" novalidate> : form 表单标签内的 novalidate表示前端不进行校验。要使得错误提示信息显示为中文的话,除了上面的自定义错误提示信息以外,还可以修改配制文件里的语言环境:LANGUAGE_CODE= 'zh-hans'

 

六、forms组件校验补充

widget参数

可以控制生成标签的各项属性

from django import widgets

使用

widget=forms.widgets.PasswordInput(attrs={'class':'form-control'}))

# 设置为密文显示,并且增加样式,可添加多个,空格隔开即可

forms组件三种校验方式

forms组件针对字段数据的校验,提供了三种类型的校验方式(可以一起使用)

  • 第一种类型:直接填写参数  max_length
  • 第二种类型:使用正则表达式        validators
  • 第三种类型:钩子函数    编写代码自定义校验规则

正则校验

要先导入一个正则校验模块

from django.core.validators import RegexValidator

class MyForm(Form):
    phone = forms.CharField(
        validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), 
     RegexValidator(r'^159[0-9]+$', '数字必须以159开头')], )

七.forms组件之全局钩子, 局部钩子(重要)

1.钩子函数

  • 全局和局部钩子都是定义在forms组件之内的函数
  • 钩子函数类似于校验的第二道关卡, 能够让我们自定义校验规则
  • 在字段所有的校验参数之后触发

2.两种钩子函数

  • 局部钩子 : 用于给单个字段增加校验规则
  • 全局钩子 : 用于给多个字段增加校验规则

3.两种钩子函数的书写方式

  • 局部钩子

def clean_字段名(self):
    字段值 = self.cleaned_data.get('字段名')
    [判断逻辑代码]
    [不通过抛出 ValidationError 异常]
    [通过则返回该字段值]
  • 全局钩子

def clean(self):
    字段值1 = self.cleaned_data.get('字段1')
    字段值2 = self.cleaned_data.get('字段2')
    [判断逻辑代码]
    [不通过抛出 ValidationError 异常]
    [通过则 return self.cleaned_data]

注意:钩子函数要写在类里面

4.钩子示例

  • 需求一 : 校验用户名不能有敏感字眼
  • 需求二 : 校验两次输入的密码需一致

 # myform.py 新增钩子函数

# 局部钩子
def clean_name(self):
    name = self.cleaned_data.get('name')
    for i in ['爱德华','sb']:
        if i in name:
            raise ValidationError(f'用户名中包含敏感词汇:"{i}"')
    else:
        return name

# 全局钩子
def clean(self):
    password = self.cleaned_data.get('password')
    re_password = self.cleaned_data.get('re_password')
    if password == re_password:
        return self.cleaned_data
    else:
        raise ValidationError('两次密码不一致')

 # views.py 文件

from app01.myform import RegisterForm


def register(request):
    if request.method == 'POST':
        form_data = RegisterForm(request.POST)  # 将前端post请求的数据传入

        if form_data.is_valid():
            return HttpResponse('校验成功')
        else:
            # 获取到 ValidationError 中的 error 信息传给前端
            error = form_data.errors.get('__all__')
            return render(request, 're_form.html', {'form': form_data,'error':error})

    form_data = RegisterForm()  # 将form空对象传给前端
    return render(request, 're_form.html', {'form': form_data})

 # re_form.html 文件

<form action="" method="post" novalidate>
{% for foo in form %}
    <p>{{ foo.label }}:{{ foo }}
        <span style="color: red; display:inline-block">{{ foo.errors.0 }}</span>
    </p>
{% endfor %}

<input type="submit" class="btn btn-block btn-success" value="注册">
{# 判断错误信息是否为None,是None就不渲染error #}
{% if error is None %}
    <span style="color: red; display:inline-block"></span>
{% else %}
  <span style="color: red; display:inline-block">{{ error }}</span>
{% endif %}
</form>

八.error错误提示为何是ul标签分析

  • 上面我们在 views.py 文件中写到的,校验时打印错误提示信息
else:
    print('检验失败')
    # 获得检验失败的错误提示 errors
    print(form_data.errors)
  • 查看打印结果 :

 

 为什么是一个ul标签格式, 一定是重写了 __str__方法

  • 我们导入 ErrorDict 来查看内部源码
from django.forms.utils import ErrorDict

发现其内部重写了__str__方法, 返回的是 as_ul方法, 该方法内部就是定义了html的格式并返回

并且还有一些其他的方法 : 比如 json 格式, data 数据, text 格式等等

而返回 as_ul 方法则是为了方便渲染模板

九.常用字段及插件

1. 初始值 : initial

username = forms.CharField(
        min_length=8,
        label="用户名",
        initial="shawn"  # 设置默认值
    )

2. 自定义错误提示 : error_massages

username = forms.CharField(
        min_length=8,
        label="用户名",
        initial="shawn",
        error_messages={
            "required": "不能为空",
            "invalid": "格式错误",
            "min_length": "用户名最短8位"
        }
    )

3. 密码框 : password

# 设置render_value在输入错误的时候, 保留输入的内容value值
pwd = forms.CharField(
        min_length=4,
        label="密码",
        widget=forms.widgets.PasswordInput(attrs={'class': 'a1'}, render_value=True)
    )

4. 反选radio

gender = forms.ChoiceField(
    choices=((1, ""), (2, ""), (3, "未知")),
    label="性别",
    initial=3,
    widget=forms.widgets.RadioSelect()
)

5. 单选checkbox

keep = forms.ChoiceField(
    label="是否记住密码",
    initial="checked",
    widget=forms.widgets.CheckboxInput()
)

6. 多选checkbox

Copyhobbies1 = forms.MultipleChoiceField(
    choices=((1, "read"), (2, "run"), (3, "play"),),
    label="爱好",
    initial=[1, 3],
    widget=forms.widgets.CheckboxSelectMultiple()
)

7. 单选Select

Copyeducation  = forms.ChoiceField(
    choices=((1, "高中"), (2, "中专"), (3, "大专"), (4, "本科"), (5, "研究生"), (6, "硕士")),
    label="学历",
    initial=3,
    widget=forms.widgets.Select(attrs={'class': 'form-control'})
)

8. 多选Select

Copyhobbies2 = forms.MultipleChoiceField(
    choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
        label="爱好",
    initial=[1, 2],
    widget=forms.widgets.SelectMultiple(attrs={'class': 'form-control'})
)

9.更多插件(了解)

插件名称 对应input
TextInput input type=“text”
PasswordInput input type=“password”
HiddenInput input type=“hidden”
NumberInput input type=“number”
EmailInput input type=“email”
URLInput input type=“url”
Textarea textarea
DateInput input type=“data”
DateTimeInput input type=“datetime”
TimeInput 只获取时分秒
CheckboxInput input type=“checkbox”
CheckboxSelectMultiple 多选按钮
Select select框
NullBooleanSelect select框 三个选项 0 1 null
SelectMultiple 可多选的select框
RadioSelect input type=“radion”
FileInput 可以查看当前目录下的所有文件。作用不大

 十、modelform组件

什么是modelform组件?

  这是一个神奇的组件,通过名字我们可以看出来,这个组件的功能就是把model和form组合起来。先来一个简单的例子来看一下这个东西怎么用:

  比如我们的数据库中有这样一张学生表,字段有姓名,年龄,爱好,邮箱,电话,住址,注册时间等等一大堆信息,现在让你写一个创建学生的页面,你的后台应该怎么写呢?

  首先我们会在前端一个一个罗列出这些字段,让用户去填写,然后我们从后天一个一个接收用户的输入,创建一个新的学生对象,保存起来。

  --用之前学的方式创建模型表,很慢很繁琐。

  --我们现在有个更方便的方法:ModelForm

使用校验性组件的目的

  • 我们学习校验性组件的目的,绝大部分是为了数据录入数据库之前的各项审核
  • forms组件使用的时候需要对照模型类编写代码,不够方便
  • forms组件的强化版本,更好用更简单更方便!!!

常用参数介绍

1 model = models.Book  # 对应的Model中的类
2 fields = "__all__"  # 字段,如果是__all__,就是表示列出所有的字段
3 exclude = None  # 排除的字段
4 labels = None  # 提示信息
5 help_texts = None  # 帮助提示信息
6 widgets = None  # 自定义插件
7 error_messages = None  # 自定义错误信息

代码展示

modelform提供了一个save方法,可以帮我们保存数据。(可以代替ORM的create和update操作)

from django import forms
from app01 import models


class MyModelForm(forms.ModelForm):
    class Meta:
        model = models.UserInfo
        fields = '__all__'
        labels = {
            'username':'用户名'
        }


def ab_mf_func(request):
    modelform_obj = MyModelForm()
    if request.method == 'POST':
        modelform_obj = MyModelForm(request.POST,instance=User_obj)  # 加上instance参数之后,.save()就是更新的意思
        if modelform_obj.is_valid():
            modelform_obj.save()  # models.UserInfo.objects.create(...)/update(...)
        else:
            print(modelform_obj.errors)
    return render(request, 'modelFormPage.html', locals())

 

posted @ 2022-12-22 19:23  莫~慌  阅读(96)  评论(0编辑  收藏  举报