閱讀目錄
一、form組件
二、cookie與session
Django中操作Cookie 摘要:
一、form組件
form介紹 我們之前在html頁面中利用form表單向后端提交數據時候,都需要對用戶的輸入進行校驗,比如校驗用戶是否輸入正確(長度、格式…),如果用戶輸入的內容有誤則需要在相應的位置顯示對應的錯誤信息來提醒用戶,提高前端的交互效率。 Django form組件就是實現上述功能的一個功能組件。 from組件主要功能有: #生成頁面可用的html標簽 #對用戶條件的數據進行校驗 #保留上次輸入內容 先來看看自己手寫注冊功能的過程:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
注:如果你對python感興趣,我這有個學習Python基地,里面有很多學習資料,感興趣的
+ Q群:
895817687
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def register ( request
) : error_msg
= "" if request
. method
== "POST" : username
= request
. POST
. get
( "name" ) pwd
= request
. POST
. get
( "pwd" ) if len ( username
) < 6 : error_msg
= "用戶名長度不能小于6位" else : return HttpResponse
( "注冊成功" ) return render
( request
, "register.html" , { "error_msg" : error_msg
} )
!DOCTYPE html
>
< html lang
= "en" >
< head
> < meta charset
= "UTF-8" > < title
> 注冊頁面
< / title
>
< / head
>
< body
>
< form action
= "/reg/" method
= "post" > < p
> 用戶名
: < input type = "text" name
= "name" > < / p
> < p
> 密碼:
< input type = "password" name
= "pwd" > < / p
> < p
> < input type = "submit" value
= "注冊" > < p style
= "color: red" > { { error_msg
} } < / p
> < / p
>
< / form
>
< / body
>
< / html
>
from django
import forms
class MyForm ( forms
. Form
) : name
= forms
. CharField
( label
= '用戶名' , max_length
= 6 ) pwd
= forms
. CharField
( label
= '密碼' , max_length
= 8 , min_length
= 4 ) def register ( request
) : form_obj
= MyForm
( ) if request
. method
== 'POST' : form_obj
= MyForm
( request
. POST
) if form_obj
. is_valid
( ) : return HttpResponse
( '注冊成功' ) return render
( request
, 'register.html' , { 'form_obj' : form_obj
} )
這里我們使用PythonConsole來測試:
依次輸入:(帶
from app01
import views
form_obj
= views
. MyForm
( { 'name' : 'hhh' , 'pwd' : '123' } )
form_obj
. is_valid
( )
form_obj
. errors
form_obj
. cleaned_data
##form_obj.is_valid() 校驗提交的信息,如果全部通過,則返回True,否則為False #from_obj.errors 查看錯誤的信息,結果是一個字典格式,key為錯誤的字段,value為錯誤的原因,注意這里面是一個列表,說明原因可以有多個,這里需要說明:所有校驗未通過的字段及錯誤信息提示都在這里放著,以鍵值對的形式存放。 ##form_obj.cleaned_data 查看校驗通過的數據,這里存放這所有校驗通過的字段及其值,以字典形式存放。 特別補充: 如果:多串字段:不進行校驗,所以不會在cleaned_data和errors中 少傳字段: 也會校驗少傳的字段,會在errors中,且該字段描述:{‘pwd’: [‘This field is required.’]} 所有Form中的字段默認都是要校驗,但是可以通過設置required = False來更過。
from django
import forms
class MyForm ( forms
. Form
) : name
= forms
. CharField
( max_length
= 6 ) pwd
= forms
. CharField
( max_length
= 8 , min_length
= 4 ) def register ( request
) : form_obj
= MyForm
( ) if request
. method
== 'POST' : form_obj
= MyForm
( request
. POST
) if form_obj
. is_valid
( ) : return HttpResponse
( '注冊成功' ) return render
( request
, 'register.html' , { 'form_obj' : form_obj
} )
< !DOCTYPE html
>
< html lang
= "en" >
< head
> < meta charset
= "UTF-8" > < title
> Title
< / title
>
< / head
>
< body
>
< form action
= "/register/" method
= "post" >
{ { { form_obj
. as_p
} } < / form
>
< / body
>
< / html
>
可以看到,前面的Name和Pwd是Django自己渲染加上的,如果想要自定義,可以在MyForm類的對應字段里加上:label='用戶名’和labe 上面是第一種渲染方式,可以看出可拓展性較差
from django
import forms
class MyForm ( forms
. Form
) : name
= forms
. CharField
( max_length
= 6 , label
= '用戶名' ) pwd
= forms
. CharField
( max_length
= 8 , min_length
= 4 , label
= '密碼' ) email
= forms
. EmailField
( label
= '郵箱' )
< !DOCTYPE html
>
< html lang
= "en" >
< head
> < meta charset
= "UTF-8" > < title
> Title
< / title
>
< / head
>
< body
>
< form action
= "/register/" method
= "post" >
< p
> { { form_obj
. name
} } < / p
>
< p
> { { form_obj
. pwd
} } < / p
>
< p
> { { form_obj
. email
} } < / p
>
< / form
>
< / body
>
< / html
>
加入input框前的名字:
< !DOCTYPE html
>
< html lang
= "en" >
< head
> < meta charset
= "UTF-8" > < title
> Title
< / title
>
< / head
>
< body
>
< form action
= "/register/" method
= "post" >
< p
> { { form_obj
. name
. label
} } { { form_obj
. name
} } < / p
>
< p
> { { form_obj
. pwd
. label
} } { { form_obj
. pwd
} } < / p
>
< p
> { { form_obj
. email
. label
} } { { form_obj
. email
} } < / p
>
< / form
>
< / body
>
< / html
>
此種方法還是有缺點,比如字段如果有100個,難道要100個全部輸嗎?所以應該還有更好的方法:
< !DOCTYPE html
>
< html lang
= "en" >
< head
> < meta charset
= "UTF-8" > < title
> Title
< / title
>
< / head
>
< body
>
< form action
= "/register/" method
= "post" >
{ % for foo
in form_obj
% } < p
> 第三種渲染方式:
{ { foo
. label
} } { { foo
} } < / p
>
{ % endfor
% }
< / form
>
< / body
>
< / html
>
這里提一下:如果想要取消前端校驗,可以在form表單中加入:novalidate 這里需要知道: 1、如果Django創建的form表單在提交的時候數據不合法(校驗不通過),則會自動保留填寫的信息,不會清空。 2、Django創建的表單是沒有提交按鈕的,需要自己創建。 接下來,添加校驗不通過時候的提示信息:只需在恰當的位置添加
< !DOCTYPE html
>
< html lang
= "en" >
< head
> < meta charset
= "UTF-8" > < title
> Title
< / title
>
< / head
>
< body
>
< form action
= "/register/" method
= "post" novalidate
>
{ % for foo
in form_obj
% } < p
> 第三種渲染方式:
{ { foo
. label
} } { { foo
} } < span
> { { foo
. errors
} } < / span
> < / p
>
{ % endfor
% } < input type = "submit" >
< / form
>
< / body
>
< / html
>
這里,提示錯誤信息在下面顯示,如果想讓它在每個字段的input框后面顯示可以加個0
< !DOCTYPE html
>
< html lang
= "en" >
< head
> < meta charset
= "UTF-8" > < title
> Title
< / title
>
< / head
>
< body
>
< form action
= "/register/" method
= "post" novalidate
>
{ % for foo
in form_obj
% } < p
> 第三種渲染方式:
{ { foo
. label
} } { { foo
} } < span
> { { foo
. errors
. 0 } } < / span
> < / p
> { % endfor
% } < input type = "submit" >
< / form
>
< / body
>
< / html
>
繼續走向個性化:提示的錯誤信息是英文,我看不懂,那就改成中文: 修改錯誤提示信息為自定義信息
from django
import forms
class MyForm ( forms
. Form
) : name
= forms
. CharField
( max_length
= 6 , label
= '用戶名' , error_messages
= { 'max_length' : '用戶名最大長度為6位' , 'required' : '用戶名必須不能為空' } ) pwd
= forms
. CharField
( max_length
= 8 , min_length
= 4 , label
= '密碼' , error_messages
= { 'max_length' : '密碼最大長度為8位' , 'min_length' : '密碼最小長度為4位' , 'required' : '用戶名必須不能為空' } ) email
= forms
. EmailField
( label
= '郵箱' , error_messages
= { 'required' : '郵箱必須不能為空' , 'invalid' : '郵箱格式不合法' } )
測試測試:
< !DOCTYPE html
>
< html lang
= "en" >
< head
> < meta charset
= "UTF-8" > < title
> Title
< / title
>
< / head
>
< body
>
< form action
= "/register/" method
= "post" novalidate
>
{ % for foo
in form_obj
% } < p
> 第三種渲染方式:
{ { foo
. label
} } { { foo
} } < span
> { { foo
. errors
. 0 } } < / span
> < / p
> { % endfor
% } < input type = "submit" >
< / form
>
< / body
>
< / html
>
這里有個小點需要說明:默認google瀏覽器的表單輸入框如果要求最大6位,就只能輸入6位最大,再多輸入沒有反應,這個為了測試方便,可以認為在每次提交前通過右鍵檢查,人為刪除限制的html代碼
常用字段與插件: 這里系統的在補充一些Form類在創建的時候會涉及到的字段和插件,自對用于對用戶請求數據的驗證,插件用于自動生成html
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
) : username
= forms
. CharField
( min_length
= 8 , label
= "用戶名" , initial
= "張三" , error_messages
= { "required" : "不能為空" , "invalid" : "格式錯誤" , "min_length" : "用戶名最短8位" } ) pwd
= forms
. CharField
( min_length
= 6 , label
= "密碼" )
PasswordInput
pwd
= forms
. CharField
( max_length
= 8 , min_length
= 4 , label
= '密碼' , error_messages
= { 'max_length' : '密碼最大長度為8位' , 'min_length' : '密碼最小長度為4位' , 'required' : '用戶名必須不能為空' } , widget
= forms
. widgets
. PasswordInput
( attrs
= { 'type' : 'password' } ) )
radioSelect 將下拉式選擇框轉變成單選形式
gender
= forms
. ChoiceField
( choices
= ( ( 1 , "男" ) , ( 2 , "女" ) , ( 3 , "保密" ) ) , label
= "性別" , initial
= 3 , widget
= forms
. widgets
. RadioSelect
( )
單選Select
class LoginForm ( forms
. Form
) : . . . hobby
= forms
. ChoiceField
( choices
= ( ( 1 , "籃球" ) , ( 2 , "足球" ) , ( 3 , "雙色球" ) , ) , label
= "愛好" , initial
= 3 , widget
= forms
. widgets
. Select
( ) )
多選Select
class LoginForm ( forms
. Form
) : . . . hobby
= forms
. MultipleChoiceField
( choices
= ( ( 1 , "籃球" ) , ( 2 , "足球" ) , ( 3 , "雙色球" ) , ) , label
= "愛好" , initial
= [ 1 , 3 ] , widget
= forms
. widgets
. SelectMultiple
( ) )
單選checkbox
class LoginForm ( forms
. Form
) : . . . keep
= forms
. ChoiceField
( label
= "是否記住密碼" , initial
= "checked" , widget
= forms
. widgets
. CheckboxInput
( ) )
多選checkbox
class LoginForm ( forms
. Form
) : . . . hobby
= forms
. MultipleChoiceField
( choices
= ( ( 1 , "籃球" ) , ( 2 , "足球" ) , ( 3 , "雙色球" ) , ) , label
= "愛好" , initial
= [ 1 , 3 ] , widget
= forms
. widgets
. CheckboxSelectMultiple
( )
choice字段注意事項 在使用選擇標簽時,需要注意choices的選項可以配置從數據庫中獲取,但是由于是靜態字段 獲取的值無法實時更新,需要重寫構造方法從而實現choice實時更新。
方式一:
from django
. forms
import Form
from django
. forms
import widgets
from django
. forms
import fields
class MyForm ( Form
) : user
= fields
. ChoiceField
( initial
= 2 , widget
= widgets
. Select
) def __init__ ( self
, * args
, ** kwargs
) : super ( MyForm
, self
) . __init__
( * args
, ** kwargs
) self
. fields
[ 'user' ] . choices
= models
. Classes
. objects
. all ( ) . values_list
( 'id' , 'caption' )
方式二:
from django
import forms
from django
. forms
import fields
from django
. forms
import models
as form_model
class FInfo ( forms
. Form
) : authors
= form_model
. ModelMultipleChoiceField
( queryset
= models
. NNewType
. objects
. all ( ) )
DjangoForm所有內置字段 Hook方法(鉤子函數) 鉤子函數分為:局部鉤子和全局鉤子 這里還需要注意:鉤子函數是在對校驗通過的字段進行進一步校驗。所以需要寫在字段下面 局部鉤子 我們在Fom類中定義 clean_字段名() 方法,就能夠實現對特定字段進行校驗。
def clean_name ( self
) : name
= self
. cleaned_data
. get
( 'name' ) if '666' in name
: self
. add_error
( 'name' , '666不對' ) return name
全局鉤子 我們在Fom類中定義 clean() 方法,就能夠實現對字段進行全局校驗。
設置兩個密碼字段pwd
= forms
. CharField
( max_length
= 8 , min_length
= 4 , label
= '密碼' , error_messages
= { 'max_length' : '密碼最大長度為8位' , 'min_length' : '密碼最小長度為4位' , 'required' : '用戶名必須不能為空' } , widget
= forms
. widgets
. PasswordInput
( attrs
= { 'type' : 'password' } ) ) confirm_pwd
= forms
. CharField
( max_length
= 8 , min_length
= 4 , label
= '密碼' , error_messages
= { 'max_length' : '密碼最大長度為8位' , 'min_length' : '密碼最小長度為4位' , 'required' : '用戶名必須不能為空' } , widget
= forms
. widgets
. PasswordInput
( attrs
= { 'type' : 'password' } ) ) def clean ( self
) : pwd
= self
. cleaned_data
. get
( 'pwd' ) confirm_pwd
= self
. cleaned_data
. get
( 'confirm_pwd' ) if pwd
!= confirm_pwd
: self
. add_error
( 'confirm_pwd' , '密碼兩次輸入不一致' ) return self
. cleaned_data
最后說說注冊的實際流程:也就是我們通過校驗成功后如何將注冊信息存入數據庫:
def reg ( request
) : form_obj
= MyForm
( ) if request
. method
== 'POST' : print ( request
. POST
) form_obj
= MyForm
( request
. POST
) if form_obj
. is_valid
( ) : return render
( request
, 'reg.html' , locals ( ) )
Cookie介紹 Cookie的由來 大家都知道HTTP協議是無狀態的。
無狀態的意思是每次請求都是獨立的,它的執行情況和結果與前面的請求和之后的請求都無直接關系,它不會受前面的請求響應情況直接影響,也不會直接影響后面的請求響應情況。
一句有意思的話來描述就是人生只如初見,對服務器來說,每次的請求都是全新的。
狀態可以理解為客戶端和服務器在某次會話中產生的數據,那無狀態的就以為這些數據不會被保留。會話中產生的數據又是我們需要保存的,也就是說要“保持狀態”。因此Cookie就是在這樣一個場景下誕生。 什么是Cookie Cookie具體指的是一段小信息,它是服務器發送出來存儲在瀏覽器上的一組組鍵值對,下次訪問服務器時瀏覽器會自動攜帶這些鍵值對,以便服務器提取有用信息。
Cookie的原理 cookie的工作原理是:由服務器產生內容,瀏覽器收到請求后保存在本地;當瀏覽器再次訪問時,瀏覽器會自動帶上Cookie,這樣服務器就能通過Cookie的內容來判斷這個是“誰”了。
查看Cookie 我們使用Chrome瀏覽器,打開開發者工具。
Django中操作Cookie
前提:
我們在后端views中視圖函數的返回參數render(),HttpResponse(),redirect(),通過查看它們的源碼發現加括號后世界上返回的是一個對象,也就是我們返回到前端的是一個對象,以前我們是直接返回,現在要用到cookie和session就得在返回前對這個對象進行操作設置Cookie
def login ( request
) : if request
. method
== 'POST' : username
= request
. POST
. get
( 'username' ) password
= request
. POST
. get
( 'password' ) if username
== 'sgt' and password
== '123' : obj
= redirect
( '/index/' ) obj
. set_cookie
( 'name' , 'sgt' , expires
= 15 ) return obj
return render
( request
, 'login.html' ) def index ( request
) : return render
( request
, 'index.html' )
當然上面的設置cookie為了演示創建過程,并沒有加任何安全措施,在實際使用中我們肯定會對他進行加密處理:做一下更安全性的操作處
obj
. set_signed_cookie
( key
, value
, salt
= '加密鹽' , max_age
= None , . . . )
參數:
key
, 鍵
value
= '' , 值
max_age
= None , 超時時間
expires
= None , 超時時間
( IE requires expires
, so
set it
if hasn't been already
. )
path
= '/' , Cookie生效的路徑,
/ 表示根路徑,特殊的:根路徑的cookie可以被任何url的頁面訪問
domain
= None , Cookie生效的域名
secure
= False , https傳輸
httponly
= False 只能http協議傳輸,無法被JavaScript獲取(不是絕對,底層抓包可以獲取到也可以被覆蓋)
獲取Cookie
request
. COOKIES
[ 'key' ] request
. get_signed_cookie
( key
, default
= RAISE_ERROR
, salt
= '' , max_age
= None )
參數:
default: 默認值 salt: 加密鹽 max_age: 后臺控制過期時間
刪除Cookie
def logout ( request
) : obj
= redirect
( "/login/" ) obj
. delete_cookie
( "user" ) return rep
只有已經登錄了,才能訪問網頁,否則跳轉到登錄頁面, 通過cookie來校驗登錄,同時通過登錄裝飾器來校驗登錄,實現登錄后自動跳轉到原來需要提示登錄的網頁
def login ( request
) : if request
. method
== 'POST' : username
= request
. POST
. get
( 'username' ) password
= request
. POST
. get
( 'password' ) if username
== 'jason' and password
== '123' : old_path
= request
. GET
. get
( 'next' ) if old_path
: obj
= redirect
( old_path
) else : obj
= redirect
( '/home/' ) obj
. set_cookie
( 'name' , 'jason' , expires
= 3600 ) return obj
return render
( request
, 'login.html' )
from functools
import wraps
def login_auth ( func
) : @wraps
( func
) def inner ( request
, * args
, ** kwargs
) : old_path
= request
. get_full_path
( ) if request
. COOKIES
. get
( 'name' ) : return func
( request
, * args
, ** kwargs
) return redirect
( '/login/?next=%s' % old_path
) return inner@login_auth
def index ( request
) : return HttpResponse
( '我是index頁面,只有登錄了才能看' ) @login_auth
def home ( request
) : return HttpResponse
( '我是home頁面,只有登錄了才能看' ) @login_auth
def xxx ( request
) : return HttpResponse
( '我是xxx頁面,只有登錄了才能看' )
Session介紹 Cookie雖然在一定程度上解決了“保持狀態”的需求,但是由于Cookie本身最大支持4096字節,以及Cookie本身保存在客戶端,可能被攔截或竊取,因此就需要有一種新的東西,它能支持更多的字節,并且他保存在服務器,有較高的安全性。這就是Session。
問題來了,基于HTTP協議的無狀態特征,服務器根本就不知道訪問者是“誰”。那么上述的Cookie就起到橋接的作用。
我們可以給每個客戶端的Cookie分配一個唯一的id,這樣用戶在訪問時,通過Cookie,服務器就知道來的人是“誰”。然后我們再根據不同的Cookie的id,在服務器上保存一段時間的私密資料,如“賬號密碼”等等。
總結而言:Cookie彌補了HTTP無狀態的不足,讓服務器知道來的人是“誰”;但是Cookie以文本的形式保存在本地,自身安全性較差;所以我們就通過Cookie識別不同的用戶,對應的在Session里保存私密的信息以及超過4096字節的文本。
另外,上述所說的Cookie和Session其實是共通性的東西,不限于語言和框架。
session基本使用(設置,獲取) #設置session:
request
. session
[ 'name' ] = 'jason'
設置session時候Django做了3件事: ①生成一個隨機的字符串 ②在Django的session表存儲隨機字符串與數據記錄 ③將隨機字符串發送給瀏覽器 #獲取session: request.session.get(‘name’)
request
. session
[ 'k1' ]
request
. session
. get
( 'k1' , None )
request
. session
[ 'k1' ] = 123
request
. session
. setdefault
( 'k1' , 123 )
del request
. session
[ 'k1' ]
request
. session
. keys
( )
request
. session
. values
( )
request
. session
. items
( )
request
. session
. iterkeys
( )
request
. session
. itervalues
( )
request
. session
. iteritems
( )
request
. session
. session_key
request
. session
. clear_expired
( )
request
. session
. exists
( "session_key" )
request
. session
. delete
( )
request
. session
. flush
( ) 這用于確保前面的會話數據不可以再次被用戶的瀏覽器訪問例如,django
. contrib
. auth
. logout
( ) 函數中就會調用它。
request
. session
. set_expiry
( value
) * 如果value是個整數,session會在些秒數后失效。
* 如果value是個datatime或timedelta,session就會在這個時間后失效。
* 如果value是
0 , 用戶關閉瀏覽器session就會失效。
* 如果value是
None , session會依賴全局session失效策略。
def login ( request
) : if request
. method
== 'POST' : username
= request
. POST
. get
( 'username' ) password
= request
. POST
. get
( 'password' ) if username
== 'sgt' and password
== '123456' : request
. session
[ 'username' ] = usernamenext_url
= request
. GET
. get
( 'next' ) if next_url
: return redirect
( next_url
) else : return redirect
( '/index/' ) return render
( request
, 'login.html' )
from functools
import wraps
def login_auth ( func
) : def inner ( request
, * args
, ** kwargs
) : next_url
= request
. get_full_path
( ) if request
. session
. get
( 'username' ) : return func
( request
, * args
, ** kwargs
) else : return redirect
( '/login/?next=%s' % next_url
) return inner@login_auth
def logout ( request
) : request
. session
. delete
( ) return redirect
( "/login/" ) @login_auth
def index ( request
) : current_user
= request
. session
. get
( "user" , None ) return render
( request
, "index.html" , { "user" : current_user
} )
FBV加裝飾器(前面cookie和session加裝飾器都是使用FBV) CBV加裝飾器:
from django
. utils
. decorators
import method_decorator
其它都一樣,主要在裝飾的方法上有區別:
class MyHome ( View
) : @method_decorator
( login_auth
) def dispatch ( self
, request
, * args
, ** kwargs
) : super ( ) . dispatch
( request
, * args
, ** kwargs
) def get ( self
, request
) : return HttpResponse
( 'get' ) def post ( self
, request
) : return HttpResponse
( 'post' )
總結
以上是生活随笔 為你收集整理的Web框架之Django_08 重要组件(form组件、cookie和session组件) 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。