什么是django表單
- django中的表單不是html中的那個(gè)表單,這里是指django有一個(gè)組件名叫表單
- 它可以通過配置去驗(yàn)證數(shù)據(jù)的合法性
- 同樣也可以通過配置生成HTML代碼.
使用表單:
- 創(chuàng)建一個(gè)
forms.py的文件,放在指定的app當(dāng)中,然后在里面寫表單。 - 如果新建了app應(yīng)用,同樣要記得在settings中添加至INSTALLED_APPS。
- 表單是通過類實(shí)現(xiàn)的,繼承自`forms.Form,然后在里面定義要驗(yàn)證的字段。
from django import forms
- 在表單中,創(chuàng)建字段跟模型是一模一樣的,但是沒有
null=True(是否接受空值NULL,默認(rèn)值False)或者blank=True(是否接受空白內(nèi)容,默認(rèn)為False)等這幾種參數(shù)了,有的參數(shù)是required=True/False(請(qǐng)求能否為空,True不能為空,默認(rèn)為True)
class RegisterForm(forms.Form):
# label 屬性是form表單中特有的屬性,代表這個(gè)字段的描述,這個(gè)字典類似于verbose_name
username = forms.CharField(label = u'用戶名',max_length = 20,min_length=3)
# 存儲(chǔ)到數(shù)據(jù)庫的密碼,是一個(gè)加密后的字符串,但是這里是通過前端傳輸過來的,并沒進(jìn)行加密
password = forms.CharField(label= u'密碼',max_length=20,min_length=8)
- 表單生成HTML元素:
# views.py
class Register(View): #注冊(cè)功能
def get(self,request):
# 如果需要使用django表單渲染html頁面
# 實(shí)例化該表單模型,傳遞給前端
form = RegisterForm()
return render(request,'register_form.html',{"form":form})
def post(self,request):
# 如果不使用django表單,需要一個(gè)一個(gè)的值取出來,并且需要自己寫對(duì)應(yīng)的驗(yàn)證
username = request.POST.get('username')
password = request.POST.get('password')
return render(request,'register_form.html',locals())
# register.html
<body>
{% if username %}
提交的post數(shù)據(jù):
{{ username }}
{{ password }}
{% else %}
<form action = "{% url "register_form" %}" method="post">
{% csrf_token %}
{{ form }} <!--會(huì)自動(dòng)識(shí)別表單屬性的 -->
<input type="submit" vlaue='注冊(cè)'>
</form>
{% endif %}
</body>


ps: 使用django的Form類生成的表單,不包含form和submit按鈕兩個(gè)標(biāo)簽,需要手動(dòng)添加。
ps : 這個(gè)模塊用得比較少,這個(gè)功能確實(shí)很雞肋,把前端該做的事情放到后臺(tái)來實(shí)現(xiàn),增加了代碼的耦合性也增加了服務(wù)器的壓力。
- 不使用表單生成元素
- 寫好對(duì)應(yīng)的html元素,HTML表單元素的
name必須和django中的表單的name保持一致,否則匹配不到。 - 后臺(tái)post方法調(diào)用
form = RegistForm(request.POST)
- 使用
is_valid()方法可以驗(yàn)證用戶提交的數(shù)據(jù)是否合法, 這個(gè)方法會(huì)返回一個(gè)bool,合法返回True, 否則返回False
form.is_valid()
-
is_bound屬性:用來表示form是否綁定了數(shù)據(jù),如果綁定了,則返回True,否則返回False。即是否為None -
cleaned_data:這個(gè)是在is_valid()返回True的時(shí)候,保存用戶提交上來的數(shù)據(jù)。
#models.py
from django.db import models
# Create your models here.
class User(models.Model):
username = models.CharField(u'用戶名',max_length= 20)
password = models.CharField(max_length = 40,verbose_name = u'密碼')
class Meta:
db_table = 'user'
managed = True
#forms.py
# -*- coding: utf-8 -*-
from django import forms
'''
forms.py的作用
它是專門編寫你的forms配置的模型
forms.py本身命名沒有要求,你可以為任意名稱, 但是我們一般約定它叫forms,代表這個(gè)文件是專門處理該APP下處理表單組件的
'''
class RegisterForm(forms.Form):
# label 屬性是form表單中特有的屬性,代表這個(gè)字段的描述,這個(gè)字典類似于verbose_name
username = forms.CharField(label = u'用戶名',max_length = 20,min_length=3)
# 存儲(chǔ)到數(shù)據(jù)庫的密碼,是一個(gè)加密后的字符串,但是這里是通過前端傳輸過來的,并沒進(jìn)行加密
password1 = forms.CharField(label= u'密碼',max_length=20,min_length=8)
password2 = forms.CharField(label= u'密碼',max_length=20,min_length=8)
#views.py
class Register(View): #注冊(cè)功能
def get(self,request):
return render(request,'register_form.html')
def post(self,request):
# 如果使用表單
form = RegisterForm(request.POST)
# is_bound 是一個(gè)屬性,它只驗(yàn)證數(shù)據(jù)字段存在不存在,不驗(yàn)證你的數(shù)據(jù)是否正確
print(form.is_bound)
# is_valid,這是一個(gè)方法,它會(huì)進(jìn)行所有的驗(yàn)證,包括是否存在,跟數(shù)據(jù)是否正確
if form.is_valid():
# 使用cleaned_data 必須執(zhí)行完is_valid 且返回為True才能獲取數(shù)據(jù),保存用戶提交上來的數(shù)據(jù)
username = form.cleaned_data['username']
password1 = form.cleaned_data['password']
password1 = md5_password(password1)
User.objects.create(username=username,password=password1)
return HttpResponse('注冊(cè)成功')
else:
return HttpResponse('數(shù)據(jù)驗(yàn)證失敗')
#register_form.html
<body>
{% if username %}
提交的post數(shù)據(jù):
{{ username }}
{{ password }}
{% else %}
<form action = "{% url "register_form" %}" method="post">
{% csrf_token %}
用戶名:<input type="text" name="username"/><br/>
密碼:<input type="password" name="password1"/><br/>
確認(rèn)密碼:<input type="password" name="password2"/><br/>
<input type="submit" vlaue='注冊(cè)'>
</form>
{% endif %}
</body>
- 上傳文件:
在相應(yīng)的模型里面定義FileField或者是ImageField類型的字段,并且設(shè)置好upload_to參數(shù)來指定上傳的路徑。
headshot = models.ImageField(u'頭像', upload_to="upload/%Y/%m/%d")
- 如果是使用ImageField,會(huì)需要安裝一個(gè)依賴Pillow, Pillow是專門做圖片處理的一個(gè)python包
pip install Pillow
- 需要在
settings.py文件中指定媒體路徑MEDIA_ROOT。
MEDIA_ROOT = "media/"
ps: 這里是媒體文件,它也是一種靜態(tài)文件,在django中,這一塊的內(nèi)容是要跟其它靜態(tài)文件分開處理的
- django 中的文件存儲(chǔ)分為兩種:
靜態(tài)文件存儲(chǔ),一般是我們的JS、css、系統(tǒng)的圖片文件等。
媒體文件存儲(chǔ),一般是用戶上傳的圖片、文件數(shù)據(jù),或大的文件或視頻等等。 - 文件上傳需要在HTML代碼中的form表單中添加
enctype="multipart/form-data"以及在views當(dāng)中,使用request.FILES來接收文件.
form = LoginUserForm(request.POST, request.FILES)
- 文件只有在保存時(shí)才會(huì)處理,數(shù)據(jù)庫保存的是文件的路徑,不會(huì)保存文件本身。
user = LoginUser(**form.clean())
user.save()
- 表單錯(cuò)誤消息:
- 表單驗(yàn)證沒有通過后,表單會(huì)產(chǎn)生一個(gè)
errors屬性,這個(gè)屬性包括所有的驗(yàn)證錯(cuò)誤信息。 - 通過
form.errors即可訪問。 - 通過
form['屬性名'].erros訪問對(duì)應(yīng)的錯(cuò)誤 - 通過
form.errors.get_json_data()或form['屬性名'].erros.get_json_data()可以將錯(cuò)誤消息轉(zhuǎn)換成json數(shù)據(jù)。 - 自定義錯(cuò)誤消息:在
Field中添加一個(gè)error_messages的dict類型的參數(shù),然后根據(jù)屬性名設(shè)置對(duì)應(yīng)的message,例如以下代碼:
password = forms.CharField(max_length=10,error_messages={'required':u'密碼不能少'})
- 必須要執(zhí)行完is_valid函數(shù),否則errors是不會(huì)包含錯(cuò)誤
- 表單自定義錯(cuò)誤消息:
系統(tǒng)自帶的表單數(shù)據(jù)錯(cuò)誤信息有時(shí)無法滿足我們的需求,比如用戶是否已經(jīng)存在了,可以在表單類中自定義錯(cuò)誤信息,在表單中,重寫方法clean_field(field是一個(gè)屬性名),可以自定義針對(duì)某一個(gè)field的驗(yàn)證機(jī)制,如果出現(xiàn)錯(cuò)誤
1)如果某個(gè)field出現(xiàn)驗(yàn)證錯(cuò)誤,通過add_error方法給指定的field添加錯(cuò)誤消息。
2)直接拋出一個(gè)raise ValidationError(message, code="屬性名")就可以了。
3)重寫clean方法會(huì)在先完成django默認(rèn)的驗(yàn)證后,再重新執(zhí)行clean方法的驗(yàn)證。
4)如果驗(yàn)證完成成功了,則直接返回當(dāng)前值。
def clean_password(self):
password = self.cleaned_data.get('password',None)
if len(password) < 6:
raise forms.ValidationError(u'password at least 6 length',code='min_length')
return password
注意:
1)如果沒有設(shè)置表單屬性,則不會(huì)出現(xiàn)相應(yīng)錯(cuò)誤:
#如果不設(shè)置max_length = 20,min_length=3等屬性,則不會(huì)出現(xiàn)錯(cuò)誤;
password1 = forms.CharField(label= u'密碼')
2)設(shè)置相關(guān)屬性:
password2 = forms.CharField(label= u'密碼',max_length=20,min_length=8)

3)通過error_messages字典來設(shè)置對(duì)應(yīng)message
username = forms.CharField(label = u'用戶名',required= True,max_length = 20,min_length=3,error_messages={'required':u'用戶名不能為空','max_length':u'最大長(zhǎng)度不能超過20個(gè)字符','min_length':u'至少3個(gè)字符'})

4)自定義錯(cuò)誤
def clean_username(self):
username = self.cleaned_data['username']
user = User.objects.filter(username=username)
if user:
# 如果這里判斷有多個(gè)錯(cuò)誤存在,則使用add_error方法
self.add_error('username',u'用戶名已存在')
# 如果只是單個(gè)錯(cuò)誤,使用raise ValidationError,否則這里raise拋出去了,后面就不能執(zhí)行了
# raise forms.ValidationError(u'用戶名已存在')
#敏感字符
if username.find('mmp') >= 0:
self.add_error('username',u'存在敏感字符')
return username #剛才這里沒有返回值 你對(duì)username處理完了就沒有了沒有了 后面的時(shí)候views.py、沒有數(shù)據(jù)傳進(jìn)去
不管那種必須要執(zhí)行完is_valid函數(shù),否則執(zhí)行相關(guān)errors是不會(huì)包含錯(cuò)誤,form類的運(yùn)行順序是init,clean,validte,save;
如果遇到類似錯(cuò)誤,比如說不能為空,最大最小長(zhǎng)度時(shí),在error_messages寫了錯(cuò)誤信息,也自定義了表單錯(cuò)誤信息,則required=True時(shí)調(diào)用error_message,否則自定義的。
完整參考代碼:
app應(yīng)用urls.py
url(r'^register/$', views.Register.as_view(), name="register_form")
models.py:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
class User(models.Model):
username = models.CharField(u'用戶名',max_length= 10)
password = models.CharField(max_length = 40,verbose_name = u'密碼')
#upload_to的設(shè)置會(huì)自動(dòng)在settings配置的media文件夾下創(chuàng)建這樣一個(gè)文件夾
#% Y / % m / % d‘被strftime()所格式化;
# ’ % Y’ 將會(huì)被格式化為一個(gè)四位數(shù)的年份, ‘ % m’ 被格式化為一個(gè)兩位數(shù)的月份’ % d’是兩位數(shù)日份。
headshot = models.ImageField(u'頭像',upload_to='upload/%Y/%m/%d',null= True)
class Meta:
db_table = 'user'
managed = True
表單forms.py:
# -*- coding: utf-8 -*-
from django import forms
from .models import User
'''
forms.py的作用
它是專門編寫你的forms配置的模型
forms.py本身命名沒有要求,你可以為任意名稱, 但是我們一般約定它叫forms,代表這個(gè)文件是專門處理該APP下處理表單組件的
'''
class RegisterForm(forms.Form):
# label 屬性是form表單中特有的屬性,代表這個(gè)字段的描述,這個(gè)字典類似于verbose_name
#根據(jù)屬性名設(shè)置對(duì)應(yīng)的message
username = forms.CharField(label = u'用戶名',required= True,min_length = 3,max_length = 10,error_messages={'required':u'用戶名不能為空','max_length':u'最大長(zhǎng)度不能超過20個(gè)字符','min_length':u'至少3個(gè)字符'})
# 存儲(chǔ)到數(shù)據(jù)庫的密碼,是一個(gè)加密后的字符串,但是這里是通過前端傳輸過來的,并沒進(jìn)行加密
password1 = forms.CharField(label= u'密碼',min_length = 8,max_length = 16)
#如果沒有進(jìn)行
password2 = forms.CharField(label= u'密碼',min_length = 8,max_length = 16)
#如果設(shè)置required= False則該字段可不填
headshot = forms.ImageField(label=u'頭像',required= False)
#表單自定義錯(cuò)誤消息:重寫方法clean_field(field是一個(gè)屬性名),可以自定義針對(duì)某一個(gè)field的驗(yàn)證機(jī)制,一個(gè)屬性一個(gè)對(duì)應(yīng)方法
def clean_username(self):
username = self.cleaned_data['username']
user = User.objects.filter(username=username)
if user:
# 如果這里判斷有多個(gè)錯(cuò)誤存在,則使用add_error方法
self.add_error('username',u'用戶名已存在')
# 如果只是單個(gè)錯(cuò)誤,使用raise ValidationError,否則這里raise拋出去了,后面就不能執(zhí)行了
# raise forms.ValidationError(u'用戶名已存在')
#敏感字符
if username.find('mmp') >= 0:
self.add_error('username',u'存在敏感字符')
return username #剛才這里沒有返回值 你對(duì)username處理完了就沒有了沒有了 后面的時(shí)候views.py、沒有數(shù)據(jù)傳進(jìn)去
視圖views.py:
class Register(View): #注冊(cè)功能
def get(self,request):
return render(request,'register_form.html')
def post(self,request):
#因?yàn)槲覀兊奈募峭ㄟ^request.FILES傳遞的,所以如果需要上傳文件,則需要把request.FILES傳遞進(jìn)去
# form = RegisterForm(request.POST)
form = RegisterForm(request.POST,request.FILES)
# is_bound 是一個(gè)屬性,它只驗(yàn)證數(shù)據(jù)字段存在不存在,不驗(yàn)證你的數(shù)據(jù)是否正確
print(form.is_bound)
# is_valid,這是一個(gè)方法,它會(huì)進(jìn)行所有的驗(yàn)證,包括是否存在,跟數(shù)據(jù)是否正確
if form.is_valid():
# 使用cleaned_data 必須執(zhí)行完is_valid 且返回為True才能獲取數(shù)據(jù),保存用戶提交上來的數(shù)據(jù)
username = form.cleaned_data['username']
password1 = form.cleaned_data['password1']
password1 = md5_password(password1)
# User.objects.create(username=username,password=password1,headshot=form.cleaned_data['headshot'])
#利用字典解包方式
params = {'username':username,'password':password1,'headshot' : form.cleaned_data['headshot']}
User.objects.create(**params)
#如果使用實(shí)例化對(duì)象方式,一定要記住save保存到數(shù)據(jù)庫中去;
# user = User(username = username,password=password1,headshot=form.cleaned_data['headshot'])
# user.save()
return HttpResponse('注冊(cè)成功')
else:
# print(form.errors)
# print(form['username'].errors)
# print(form['username'].errors.as_json())
# print(form.errors.as_json())
return render(request,'register_form.html',locals())
html頁面代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>register注冊(cè)表單頁面</title>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
{% if username %}
提交的post數(shù)據(jù):
{{ username }}
{{ password }}
{% else %}
<p>用戶注冊(cè)</p>
{{ form.errors }}
<!--如果表單需要上傳文件,需要設(shè)置enctype="multipart/form-data" 否則文件無法找到 -->
{#{% url 'register_form' %} 通過name找到urls配置的對(duì)應(yīng)視圖#}
<form action = "{% url 'register_form' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
用戶名:<input type="text" name="username"/><br/>
密碼:<input type="password" name="password1"/><br/>
確認(rèn)密碼:<input type="password" name="password2"/><br/>
上傳頭像:<input type="file" name = "headshot" /><br/>
<input type="submit" value="注冊(cè)"/>
</form>
{% endif %}
</body>
</html>
學(xué)以致用
需求背景:
使用表單寫一個(gè)用戶注冊(cè)
1)注冊(cè)需要有頭像、用戶名、密碼(密碼存儲(chǔ)是加密的)、性別、出生年月
2)需要做數(shù)據(jù)驗(yàn)證,字符長(zhǎng)度, 是否允許為空等判斷
3)使用自定義異常驗(yàn)證用戶名是否存在
4)使用自定義異常驗(yàn)證兩次密碼是否相同
- 分析
- 需要數(shù)據(jù)庫創(chuàng)建一個(gè)用戶表,包含用戶名,密碼,性別,出生年月,頭像字段。--在models.py中需要定義一個(gè)用戶模型類;
- 關(guān)于數(shù)據(jù)驗(yàn)證,對(duì)于字符長(zhǎng)度,是否允許為空,可以用表單自帶的錯(cuò)誤消息form.errors前端訪問,或者用erros_messages在屬性里面設(shè)置;
- 使用自定義異常驗(yàn)證則用在表單中,重寫方法clean_field(field是一個(gè)屬性名),理清form執(zhí)行順序。
模型models.py:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
class User(models.Model):
username = models.CharField(u'用戶名',max_length= 10)
password = models.CharField(max_length = 40,verbose_name = u'密碼') #存儲(chǔ)數(shù)據(jù)庫是用的md5加密后的所以設(shè)置長(zhǎng)點(diǎn);
sex = models.CharField(u'性別',max_length = 4,default='boy',null=True)
birthday = models.DateField(u'出生日期',auto_now_add=True)
headshot = models.ImageField(u'頭像',upload_to='upload/%Y/%m/%d',null= True)
class Meta:
db_table = 'user'
managed = True
表單forms.py:
# -*- coding: utf-8 -*-
from django import forms
from .models import User
class RegisterForm(forms.Form):
username = forms.CharField(label = u'用戶名',required= True,min_length = 3,max_length = 12,error_messages={'required':u'用戶名不能為空','max_length':u'最大長(zhǎng)度不能超過12個(gè)字符','min_length':u'至少3個(gè)字符'})
password = forms.CharField(label= u'密碼',min_length = 8,max_length = 16,error_messages={'required':u'密碼不能為空','max_length':u'最大長(zhǎng)度不能超過16個(gè)字符','min_length':u'至少8個(gè)字符'})
password2 = forms.CharField(label= u'密碼',min_length = 8,max_length = 16,error_messages={'required':u'密碼不能為空','max_length':u'最大長(zhǎng)度不能超過16個(gè)字符','min_length':u'至少8個(gè)字符'})
sex = forms.CharField(label=u'性別',max_length = 4,required= False)
birthday = forms.DateField(label=u'出生日期')
headshot = forms.ImageField(required= False)
def clean_username(self):
username = self.cleaned_data['username']
user = User.objects.filter(username=username)
if user:
# self.add_error('username',u'用戶名已存在')
raise forms.ValidationError(u'用戶名已存在')
return username
def clean_password2(self): #不能用clean_password,因?yàn)榧虞d這個(gè)時(shí)候,password2還沒加載出來,是沒有值的。
password = self.cleaned_data['password']
password2 = self.cleaned_data['password2']
if password != password2:
raise forms.ValidationError(u'兩次密碼不一致')
return password
頁面代碼:
<body>
<h2>用戶注冊(cè)</h2>
{{ form.errors }}<!--form.errors訪問錯(cuò)誤信息-->
<form action = "{% url 'register_form' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
用戶名:<input type="text" name="username"/><br/>
密碼:<input type="password" name="password"/><br/>
確認(rèn)密碼:<input type="password" name="password2"/><br/>
性別:
<input type="radio" name="sex">男
<input type="radio" name="sex">女
<br/>
上傳頭像:<input type="file" name = "headshot" /><br/>
出生日期:<input type="text" name="birthday"/><br/>
<input type="submit" value="注冊(cè)"/>
</form>
</body>
應(yīng)用urls.py:
# -*- coding:utf-8 -*-
from django.conf.urls import url,include
from TestApp import views
import views
urlpatterns = [
url(r'^register/$', views.Register.as_view(), name="register_form")
]
視圖views.py
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.shortcuts import render,redirect,reverse
from django.views import View
from django.http import HttpResponse
import hashlib
from .models import User
import uuid
from .forms import *
# Create your views here.
def md5_password(password): #使用md5加密
return hashlib.md5(password).hexdigest()
class Register(View): #注冊(cè)功能
def get(self,request):
return render(request,'register_form.html')
def post(self,request):
form = RegisterForm(request.POST,request.FILES)
# is_bound 是一個(gè)屬性,它只驗(yàn)證數(shù)據(jù)字段存在不存在,不驗(yàn)證你的數(shù)據(jù)是否正確
print(form.is_bound)
# is_valid,這是一個(gè)方法,它會(huì)進(jìn)行所有的驗(yàn)證,包括是否存在,跟數(shù)據(jù)是否正確
if form.is_valid():
# 使用cleaned_data 必須執(zhí)行完is_valid 且返回為True才能獲取數(shù)據(jù),保存用戶提交上來的數(shù)據(jù)
username = form.cleaned_data['username']
password = form.cleaned_data['password']
password = md5_password(password)
birthday = form.cleaned_data['birthday']
print(len(password),password,birthday)
#利用字典解包方式
params = {'username':username,'password':password,'birthday':birthday,'headshot' : form.cleaned_data['headshot']}
User.objects.create(**params)
#如果使用實(shí)例化對(duì)象方式,一定要記住save保存到數(shù)據(jù)庫中去;
# user = User(username = username,password=password1,headshot=form.cleaned_data['headshot'])
# user.save()
return HttpResponse('注冊(cè)成功')
else:
return render(request,'register_form.html',locals())
頁面效果:




重點(diǎn)
注意驗(yàn)證流程與邏輯。