Django2_07_Form表单验证

一些基本概念

HTML表单

在HTML中,表单的作用是收集 HTML 特殊标签的内容,
就是在 <form> ... </ form>标签中间可以由访问者添加类似于文本,选择或者一些控制模块等等。然后这些内容将会被送到服务端

某些表单的元素 - 文本输入和复选框 - 非常简单而且内建于HTML本身。其它的表单会复杂些; 例如弹出一个日期选择对话框的界面,允许你移动滚动条的界面,使用JavaScript和CSS以及HTML表单<inpt/>元素来实现操作控制的界面。

一个有效的表单必须指定两样东西:

目的地:响应用户输入数据的URL,就是数据要提交到哪儿
方式:发送数据的HTTP方法,如: POST GET 等方法

Django Form

可以实现下列功能:

  • 验证前端表单发送的请求数据,并返回错误信息,错误信息内容支持自定制。
  • 生成前端表单 HTML 相关标签,并且自定义生成表单标签的属性。
  • 保留表单上用户上次输入的内容。
  • 添加表单的默认值。

验证表单数据

==处理表单时只会用到 GETPOST 方法

表单系统的核心部分是Django的 Form 类。

Django的 Model 描述一个对象的逻辑结构,行为以及展现给我们的方式,与此类似,Form 类描述一个表单并决定它是如何工作和展现的。

就像 Model 类的属性映射到数据库的字段一样,表单类的字段会映射到 HTML 的 <input> 表单的元素。( ModelForm 通过一个表单映射模型类的字段到HTML表单的 <input> 元素;

Django的Admin站点就是基于这个实现的)。

基本的流程:

  1. 导入 forms

    在 views.py 文件中,或者在项目的其他你认为规范的任意文档中导入

from django import forms

  1. 创建一个自定义的类,必须继承 forms.Form 比如创建一个名字叫 AddProductForm 的类
class AddProductForm(forms.Form):
    pass  

  1. 在自定义的 Form 类里定义需要验证的字段名及相关属性

表单的字段本身也是类,它们管理表单的数据并在表单提交时进行验证。

Django 的 DateField 和 FileField 处理的数据类型都是属于对象,必须完成不同的处理。

  • 这里的每个字段都对应了一个正则表达式,用来处理字段对应的数据。

  • 这里的字段名必须和前端页面里的 input 框内的 <mark style="box-sizing: border-box;">name 属性的值一致</mark>。
    <mark style="box-sizing: border-box;">并且要和数据库中的字段名一致</mark>,这样有利于对接收到的数据进行处理,后面的例子中会有。

  • 这里<mark style="box-sizing: border-box;">设置的字段数量也必须和前端表单中的标签元素数量一样</mark>。少了,Django 会因收不到数据
    而永远对少了的字段通过不了验证;多了,无法对多出的数据其进行验证。

    定义一个字段是利用 forms 里的 fields 类,进行实例话一个对象实现的。

    利用一个简单的小示例来说明一下:

    class AddProductForm(forms.Form):
      prod_name = forms.CharField(
          required=True,
      )    
    
    
    • prod_name 就是 <input name="prod_name"/> 标签类的 name 属性的值 "prod_name",同时也应该对应数据库中名为 : prod_name 的字段
    • fields.CharField 表示这是一个字符字段,接受到的数据类型是字符。当然还有其他类型,后面会专门介绍。
    • required=True 表示此字段是必填项,这也是默认的值。False 是可以为空。

    这样一个对字符类型的数据进行验证的表单字段就创建完了。

  1. 使用刚才创建的 Form 实例,在视图函数中使用刚才创建的 Form 实例对数据进行验证
# 首先实例化一个对象。要对数据进行验证,当然是需要 POST 方法了,所以把 request.POST 对象直接传进去
form_obj = AddProductForm(request.POST) 

# form_obj.is_valid 方法会返回是否验证通过,通过为 真,不通过为假
# form_data = obj.cleaned_data 就是验证通过后的数据
if  form_obj.is_valid():
    print("验证通过后的数据:",form_obj.cleaned_data)

    # 因为刚才我们强调的字段的名字要和数据库中的字段名字一致,所以我们可以之间把清理过的数据存入到数据库中。
    models.ProductionLine.objects.create(**form_obj.cleaned_data) 
    return redirect("/production_info/")
else:
    # 验证失败,给前端返回错误信息。
    return render(request,'add_production_form.html',{'form_obj':form_obj})

  1. 前端接收返回的错误信息

    所用的错误信息都在 form_obj 的实例化对象中的 errors 对象中

    errors 对象的值实际上是一组 HTML 标签和内容

        <ul class="errorlist">
            <li>name  # 每个 li 对应了一个 字段 
                <ul class="errorlist">
                    <li>This field is required.</li>
                </ul>
            </li>
            <li>line_number
                <ul class="errorlist">
                    <li>This field is required.</li>
                </ul>
            </li>
            <li>date
                <ul class="errorlist">
                    <li>日期不能为空</li>
                </ul>
            </li>
        </ul>
    
    

    HTML 部分

    form_obj.字段名.0 会取到字段的错误信息中的字符串内容

    <form action="{% url 'add' %}" method="post">
        <div class="edit_window">
            业务名称
            <input type="text" name="pord_name" value="">
            <span>{{ form_obj.errors.name.0 }}</span>
        </div>
        <div class="edit_window">
            业务线
            <input type="text" name="line_number">
            <span>{{ form_obj.errors.line_number }}</span>
        </div>
        <div class="edit_window">
            上线日期
            <input type="text" name="date" value="">
            <span>{{ form_obj.errors.date }}</span>
        </div>
        <div class="edit_window">
            <input class=".btn" type="submit" value="提交">
            <input class=".btn-lg"  type="button" value="取消"/>
        </div>
    </form> 
    
    

前端显示的错误信息效果

[图片上传失败...(image-315baf-1561038656751)]

基本验证的流程就是这样子的。

forms 字段的类型

这里仅介绍一些常用的,更多的请参考: Django 官网

  • CharField 字符串 (普通文字) EmailField 字符串(邮箱格式) IntegerField 字符串(数字格式) GenericIPAddressField 字符串(IP格式,v4和v6) FileField 文件对象
  • DateField 时间对象 RegexField 字符串(自定义) ChoiceField 多选

定制错误信息

Form 类代码:

class AddProductForm(forms.Form):
    name = forms.CharField(
    min_length    =3,   # 可以定义字符串的最小和最大长度
    max_length    =18,
    error_messages={
        'required':  '业务名称不能为空',       # 自定义错误信息
        'min_length':  '业务名不能小于3个字符',
        # 'invalid': "无效的名称",
    },
)    

自动生成自定制的表单给前端,并且可以保留用户上次输入的内容

Form 类代码:

class AddProductForm(forms.Form):
      name = forms.CharField(
          required=True,

          # 自动生成 HTML 标签,并用 attrs 指定标签属性
          widget=widgets.TextInput(attrs={'class':'form-control', "k": "v"),
      )  

视图代码:

GET

实例化一个 Form 对象,不传任何参数,创建的对象就会有自动生成的 HTML 标签,返回给前端即可。
<mark style="box-sizing: border-box;">这种情况下,表单里并没有绑定任何数据,此时前端看到的表单里是空内容</mark>。

POST

实例化一个 Form 对象时,传入一个或者多个数据对象,就会把数据绑定到表单上。
此时再返回给前端,前端就会保留上次输入的信息了
def add_production(request):
    if request.method == "GET":
        # 实例化一个 Form 对象,不传任何参数,创建的对象就会有自动生成的 HTML 标签,返回给前端即可。
        # 这种情况下,表单里并没有绑定任何数据,此时前端看到的表单里是空内容。
        form_obj = AddProductForm()
        return render(request, 'add_production_form.html', {'form_obj': form_obj})
    elif request.method == "POST":
        form_obj = AddProductForm(request.POST)
        return render(request, 'add_production_form.html', {'form_obj': form_obj})

HTML 代码:

<form action="{% url 'add' %}" method="post" novalidate>
    {% csrf_token %}
    <div class="form-group">
        <label>业务名称</label><br><br>
        {{ form_obj.name }}  # 自动生成表单,同时可以保留上次输入的内容
        <span>{{ form_obj.errors.name.0 }}</span>  # 返回的错误信息
    </div>
</form>            

widgets (插件)

常用的插件:

示例:

  • Form 代码:
class TestForm(forms.Form):
    input_fied = forms.CharField(
        widgets.TextInput
    ) 

    input_pass_field =  forms.CharField(
        widgets.PasswordInput 
    )  

    textarea_field = fields.CharField(
        widgets.Textarea 
    )  

    # 单选下拉框
    select_field = forms.CharField(
        # 单选的可以用 CharFiled
        widget=widgets.Select(choices=[(1,"上海"),(2,"北京"),(3,"广州"),])
    )

    # 多选下拉框
    # 多选必须用 ChoiceFiled,因为选择的值是个列表
    choices_field = forms.ChoiceField(
        # 在多选下拉框中,forms.ChoiceField 就有 choices 字段,
        # 所有不会再用 widgets.SelectMultiple的字段了
        choices=[(1, "上海"), (2, "北京"), (3, "广州")],
        widget=widgets.SelectMultiple()
    )

    radios_field = forms.IntegerField(
        widget=widgets.RadioSelect(choices=[(1,'上海'),(2,'北京'),])
    )

    # 单个选择框,打对勾的那种
    checkbox_field = forms.CharField(
        widget=widgets.CheckboxInput()
    )

    # 多个选择框
    check_field = forms.ChoiceField(
        choices=[(1, '上海'), (2, '北京'), ],
        widget=widgets.CheckboxSelectMultiple()
    )

    # 文件对象
    file_field = forms.FileField()

  • 视图函数代码:
def test(request):

    if request.method == "GET":
        form_obj = TestForm()
        return render(request,'test.html',{'form_obj':form_obj})

    else:
        # obj = TestForm(request.POST,files=request.FILES)
        # if obj.is_valid():
        #     file_obj = obj.cleaned_data['n8']
        #     file_obj.name # 上传的文件名称
        #     f = open(file_obj.name,'wb')
        #     for chunk in file_obj.chunks():
        #         f.write(chunk)
        #     f.close()
        # else:
        #     pass

        # import os
        # file_obj = request.FILES.get('n8')
        # print(type(file_obj))
        # # file_obj.size
        # f = open(file_obj.name, 'wb')
        # for chunk in file_obj.chunks():
        #     f.write(chunk)
        # f.close()
        return redirect('/test')

给表单添加默认值

def test(request):

    if request.method == "GET":
        form_obj = TestForm(
            # initial  表明了需要添加字段的默认值,
            initial={'select_field': 2,'choices_field ': [1,3],'radios_field':2}
        )
        return render(request,'test.html',{'form_obj':form_obj})

·

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352

推荐阅读更多精彩内容