Django教程--Form表单

Django教程--Form表单

前面我们已经了解如何在django中使用GET、POST传递数据,但是我们并没有对用户提交的数据进行验证(为何需要验证大家都懂得),对于验证我们可以自己写相关代码进行判断(最基本也有效的方法),但是django作为一个集大成的全面框架,为我们提供了一种更优雅简单的方式,那就是今天的主角--Form,Form不仅仅具有数据验证功能,还可以生成用于提交的html标签,接下来我们就来了解一下如何使用Form。


Form基本使用

按照惯例,我们先来段代码演示如何使用,接着前面的PostParams工程,我们在app目录下新建forms.py文件,用来存放Form表单的代码,代码如下:

from django import forms


class RegisterForm(forms.Form):
    username=forms.CharField(label='username',max_length=20,required=True,error_messages={'required':'username cannot be null'})
    password=forms.CharField(label='password',widget=forms.PasswordInput,required=True,error_messages={'required':'password cannot be null'})
    email=forms.EmailField(label='email',required=True,
                            error_messages={'required': u'email cannot be null','invalid': u'email format is wrong'},
                            widget=forms.TextInput(attrs={'class': "form-control", 'placeholder': u'email'}))
    image=forms.ImageField(label='headimage',required=False)

从代码可以看到,使用Form我们需要定义一个类继承自forms.Form,然后定义几个变量(forms以经为什么提供了多种输入框,甚至Email都有,对于Email会自己做个正则表达判断),需要注意变量名对应html表单的name属性,每个属性有不同的校验规则(下面会仔细介绍,暂时先具体看下用法),我们还可以自定义每个属性的widget和label(用于生成html标签)以及校验错误返回的信息。
接下来我们在views.py里新建一个视图函数代码如下:

def form_view(request):
    if request.method=='GET':
        register_form=RegisterForm()
        return render(request,'form.html',{'form':register_form})
    else:
        register_form=RegisterForm(request.POST or None,request.FILES or None)
        if register_form.is_valid():
            return HttpResponse('username:'+register_form.cleaned_data['username']+'&password:'+register_form.cleaned_data['password']+'&email:'+register_form.cleaned_data['email'])
        else:
            return render(request, 'form.html', {'form': register_form})

当然我们也要建立对应的模板文件form.html代码如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post" enctype="multipart/form-data" >
    {%csrf_token%}
    <p>{{form.username.label}}:{{form.username}}</p>
    {%if form.errors.username%}
    {{form.errors.username}}
    {%endif%}
    <p>{{form.password.label}}:{{form.password}}</p>
     {%if form.errors.password%}
    {{form.errors.password}}
    {%endif%}
    <p>{{form.email.label}}:{{form.email}}</p>
       {%if form.errors.email%}
    {{form.errors.email}}
    {%endif%}
    <P>{{form.image.label}}:{{form.image}}</P>
     {%if form.errors.image%}
    {{form.errors.image}}
    {%endif%}

    <input type="submit" value="Submit">
</form>

</body>
</html>

然后在urls.py中添加拦截

from django.conf.urls import patterns, include, url
from django.contrib import admin

from app.views import params_test, params_test_reg, params_post, form_view

urlpatterns = patterns('',
    # Examples:
    # url(r'^$', 'PostParams.views.home', name='home'),
    # url(r'^blog/', include('blog.urls')),

    url(r'^admin/', include(admin.site.urls)),
    url(r'^params_test/$',params_test),
    url(r'^params_test_reg/str(?P<str>\w+)page(?P<page>\d+)/$',params_test_reg),
    url(r'^post/$',params_post),
    url(r'^form/$',form_view),
)

启动服务器,输入http://127.0.0.1:8000/form/即可看到对应的表单,输入相关信息然后提交,如果满足验证要求,就会返回输入信息,当不满足的时候就会重新跳回表单界面并提示错误信息。

Form表单代码解析

上面我们已经展示了如何使用Form表单,接下来我们就来分析下上述代码并讲解Form。
先看Forms.py代码,Form为我们提供了格式各样的Field,用于应对各种各样的输入场景,例如本代码里面首先定义了四个需要提交的属性,每个属性各不相同,例如username需要文本输入框使用CharField,password同样需要文本输入框但是需要密码输入框,所以需要设置widget为密码输入框,对于email需要对输入的格式进行验证是否为正确的邮箱,所以需要EmailField,而对于图像文件则有对应的ImageField,对于这些属性我们还可以通过required指定哪些是必须输入(默认为True),我们还可以通过指定error_messages验证错误时显示的信息。
定义好表单后我们需要在视图函数中使用该表单,例如本例中先判断是否为get请求,如果为get请求就创建表单然后传给模板,在模板中我们只需使用{{}}标签输出表单对应的属性,然后就会自动渲染出该属性对应的widget的代码,我们可以更改widget甚至自定义widget来达到自己的个性化网页输入需求,当然我们还要判断当前表单是否有验证错误,如果发生错误该表单的errors即会有对应的错误信息(如form.errors.username)。
当我们提交表单的时候,视图函数通过request.POST(如果提交方式为POST,为GET提交请使用request.GET),request.FILES(如果提交包含文件)自动创建好表单,然后调用is_valid()进行验证表单输入,没有错误则返回输入信息,发生错误就返回表单提交页面并显示错误信息。
也许有人会说这种提交表单的方式过于笨重(在html模板中使用表单属性),不够灵活,有时候灵活和简便是不可多得的,框架提供功能越多,可能导致其更为笨重,但是这里我们也可以不使用这种笨重的方式,我们只需要将html表单里属性的name定义为和Form变量名一样即可,然后可以同样在后台创建接收输入的表单,例如本例html代码可以更改为

<form method="post" enctype="multipart/form-data" >
 {%csrf_token%}
    <p>username:<input id="id_username" maxlength="20" name="username" type="text" /></p>
    
    <p>password:<input id="id_password" name="password" type="password" /></p>
     
    <p>email:<input class="form-control" id="id_email" name="email" placeholder="email" type="text" /></p>
       
    <P>headimage:<input id="id_image" name="image" type="file" /></P>
     

    <input type="submit" value="Submit">
</form>

后台视图函数不需要任何更改也可正常使用。
对于Form验证Django为我们提供了一些通用的验证机制,如验证属性是否为空(required),长度验证(max_length,min_length),甚至电话号码email验证(EmailField,PhoneField)。

Form自定义验证

form验证流程


这里写图片描述

1.函数full_clean()依次调用每个field的clean()函数,该函数针对field的max_length,unique等约束进行验证,如果验证成功则返回值,否则抛出ValidationError错误。如果有值返回,则放入form的cleaned_data字典中。
2.如果每个field的内置clean()函数没有抛出ValidationError错误,则调用以clean_开头,以field名字结尾的自定义field验证函数。验证成功和失败的处理方式同步骤1。
3.最后,调用form的clean()函数——注意,这里是form的clean(),而不是field的clean()——如果clean没有错误,那么它将返回cleaned_data字典。
4.如果到这一步没有ValidationError抛出,那么cleaned_data字典就填满了有效数据。否则cleaned_data不存在,form的另外一个字典errors填上验证错误。在template中,每个field获取自己错误的方式是:{{ form.username.errors }}。
5.最后,如果有错误is_valid()返回False,否则返回True。
对于某些自定义验证规则我们可以通过重载上述clean_field函数或者clean函数来实现,例如对于用户注册需要验证用户名是否存在,两次密码输入是否相等,我们可以通过以下代码实现

from django import forms


class RegisterForm(forms.Form):
    username=forms.CharField(label='username',max_length=20,required=True,error_messages={'required':'username cannot be null'})
    password=forms.CharField(label='password',widget=forms.PasswordInput,required=True,error_messages={'required':'password cannot be null'})
    password1 = forms.CharField(label='password1', widget=forms.PasswordInput, required=True,
                               error_messages={'required': 'password cannot be null'})
    email=forms.EmailField(label='email',required=True,
                            error_messages={'required': u'email cannot be null','invalid': u'email format is wrong'},
                            widget=forms.TextInput(attrs={'class': "form-control", 'placeholder': u'email'}))
    image=forms.ImageField(label='headimage',required=False)

    def clean_username(self):
        user = self.cleaned_data.get('username')
        if user == 'cc':
            raise forms.ValidationError('username exsite!')

        return user

    def clean(self):
        cleaned_data = self.cleaned_data
        pwd = cleaned_data['password']

        pwd2 = cleaned_data['password1']
        print(pwd, pwd2)
        if pwd != pwd2:
            raise forms.ValidationError('the password you input is not same')
        return cleaned_data

我们重载clean_username函数来判断提交用户名是否为‘cc’,如果相等则验证失败并提示错误(通过 raise forms.ValidationError('username exsite!')来实现),然后重载clean函数来判断两次输入密码是否相同。

Form文件接收

在上述例子中虽然使用了ImageField,但是并没有演示如何在视图函数中接收文件,对于文件来说我们同样可以通过form.cleaned_data['image']来获取,然后可以获取上传文件的信息和实际文件并可以保存下来,代码如下:

  myFile=register_form.cleaned_data['image']
            destination = open(os.path.join(BASE_DIR, myFile.name), 'wb+')
            for chunk in myFile.chunks():
                destination.write(chunk)
            destination.close()

和前一节的文件提交类似。


经过本节,我们了解了Form的基本使用--创建,渲染以及数据验证,其实Form的功能远不止于此,在后面我们会发现和Model结合Form具有更强大的功能,你只需要少量代码即可完成大量功能,这就是Django这种集大成框架的优势(当然会损失点灵活性),甚至就是python的宗旨(life is short,you need python),今天我们先了解到这里,谢谢大家支持。

参考链接

1.https://www.cnblogs.com/ccorz/p/5868380.html

最近参加支付宝小程序比赛需要访问量,麻烦各位看官有空复制下面的话打开支付宝,搜索栏粘贴,在此多谢各位支持了

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

推荐阅读更多精彩内容