Python學習-基礎篇17 Django-進階

 

分頁

Django的分頁器(paginator)

view

from django.shortcuts import render,HttpResponse

# Create your views here.
from app01.models import *
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

def index(request):

    '''
    批量導入數據:

    Booklist=[]
    for i in range(100):
        Booklist.append(Book(title="book"+str(i),price=30+i*i))
    Book.objects.bulk_create(Booklist)
    '''

    '''
分頁器的使用:

    book_list=Book.objects.all()

    paginator = Paginator(book_list, 10)

    print("count:",paginator.count)           #數據總數
    print("num_pages",paginator.num_pages)    #總頁數
    print("page_range",paginator.page_range)  #頁碼的列表



    page1=paginator.page(1) #第1頁的page對象
    for i in page1:         #遍歷第1頁的全部數據對象
        print(i)

    print(page1.object_list) #第1頁的全部數據


    page2=paginator.page(2)

    print(page2.has_next())            #是否有下一頁
    print(page2.next_page_number())    #下一頁的頁碼
    print(page2.has_previous())        #是否有上一頁
    print(page2.previous_page_number()) #上一頁的頁碼



    # 拋錯
    #page=paginator.page(12)   # error:EmptyPage

    #page=paginator.page("z")   # error:PageNotAnInteger

    '''


    book_list=Book.objects.all()

    paginator = Paginator(book_list, 10)
    page = request.GET.get('page',1)
    currentPage=int(page)


    try:
        print(page)
        book_list = paginator.page(page)
    except PageNotAnInteger:
        book_list = paginator.page(1)
    except EmptyPage:
        book_list = paginator.page(paginator.num_pages)


    return render(request,"index.html",{"book_list":book_list,"paginator":paginator,"currentPage":currentPage})

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" 
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <div class="container"> <h4>分頁器</h4> <ul> {% for book in book_list %} <li>{{ book.title }} -----{{ book.price }}</li> {% endfor %} </ul> <ul class="pagination" id="pager"> {% if book_list.has_previous %} <li class="previous"><a href="/index/?page={{ book_list.previous_page_number }}">上一頁</a></li> {% else %} <li class="previous disabled"><a href="#">上一頁</a></li> {% endif %} {% for num in paginator.page_range %} {% if num == currentPage %} <li class="item active"><a href="/index/?page={{ num }}">{{ num }}</a></li> {% else %} <li class="item"><a href="/index/?page={{ num }}">{{ num }}</a></li> {% endif %} {% endfor %} {% if book_list.has_next %} <li class="next"><a href="/index/?page={{ book_list.next_page_number }}">下一頁</a></li> {% else %} <li class="next disabled"><a href="#">下一頁</a></li> {% endif %} </ul> </div> </body> </html>

擴展

def index(request):


    book_list=Book.objects.all()

    paginator = Paginator(book_list, 15)
    page = request.GET.get('page',1)
    currentPage=int(page)

    #  若是頁數十分多時,換另一種顯示方式
    if paginator.num_pages>30:

        if currentPage-5<1:
            pageRange=range(1,11)
        elif currentPage+5>paginator.num_pages:
            pageRange=range(currentPage-5,paginator.num_pages+1)

        else:
            pageRange=range(currentPage-5,currentPage+5)

    else:
        pageRange=paginator.page_range


    try:
        print(page)
        book_list = paginator.page(page)
    except PageNotAnInteger:
        book_list = paginator.page(1)
    except EmptyPage:
        book_list = paginator.page(paginator.num_pages)


    return render(request,"index.html",locals())

自定義分頁器

"""
分頁組件使用示例:

    obj = Pagination(request.GET.get('page',1),len(USER_LIST),request.path_info)
    page_user_list = USER_LIST[obj.start:obj.end]
    page_html = obj.page_html()

    return render(request,'index.html',{'users':page_user_list,'page_html':page_html})


"""


class Pagination(object):

    def __init__(self,current_page,all_count,base_url,per_page_num=2,pager_count=11):
        """
        封裝分頁相關數據
        :param current_page: 當前頁
        :param all_count:    數據庫中的數據總條數
        :param per_page_num: 每頁顯示的數據條數
        :param base_url: 分頁中顯示的URL前綴
        :param pager_count:  最多顯示的頁碼個數
        """

        try:
            current_page = int(current_page)
        except Exception as e:
            current_page = 1

        if current_page <1:
            current_page = 1

        self.current_page = current_page

        self.all_count = all_count
        self.per_page_num = per_page_num

        self.base_url = base_url

        # 總頁碼
        all_pager, tmp = divmod(all_count, per_page_num)
        if tmp:
            all_pager += 1
        self.all_pager = all_pager


        self.pager_count = pager_count
        self.pager_count_half = int((pager_count - 1) / 2)

    @property
    def start(self):
        return (self.current_page - 1) * self.per_page_num

    @property
    def end(self):
        return self.current_page * self.per_page_num

    def page_html(self):
        # 若是總頁碼 < 11個:
        if self.all_pager <= self.pager_count:
            pager_start = 1
            pager_end = self.all_pager + 1
        # 總頁碼  > 11
        else:
            # 當前頁若是<=頁面上最多顯示11/2個頁碼
            if self.current_page <= self.pager_count_half:
                pager_start = 1
                pager_end = self.pager_count + 1

            # 當前頁大於5
            else:
                # 頁碼翻到最後
                if (self.current_page + self.pager_count_half) > self.all_pager:
                    pager_end = self.all_pager + 1
                    pager_start = self.all_pager - self.pager_count + 1
                else:
                    pager_start = self.current_page - self.pager_count_half
                    pager_end = self.current_page + self.pager_count_half + 1

        page_html_list = []

        first_page = '<li><a href="%s?page=%s">首頁</a></li>' % (self.base_url,1,)
        page_html_list.append(first_page)

        if self.current_page <= 1:
            prev_page = '<li class="disabled"><a href="#">上一頁</a></li>'
        else:
            prev_page = '<li><a href="%s?page=%s">上一頁</a></li>' % (self.base_url,self.current_page - 1,)

        page_html_list.append(prev_page)

        for i in range(pager_start, pager_end):
            if i == self.current_page:
                temp = '<li class="active"><a href="%s?page=%s">%s</a></li>' % (self.base_url,i, i,)
            else:
                temp = '<li><a href="%s?page=%s">%s</a></li>' % (self.base_url,i, i,)
            page_html_list.append(temp)

        if self.current_page >= self.all_pager:
            next_page = '<li class="disabled"><a href="#">下一頁</a></li>'
        else:
            next_page = '<li><a href="%s?page=%s">下一頁</a></li>' % (self.base_url,self.current_page + 1,)
        page_html_list.append(next_page)

        last_page = '<li><a href="%s?page=%s">尾頁</a></li>' % (self.base_url,self.all_pager,)
        page_html_list.append(last_page)

        return ''.join(page_html_list)

中間件

中間件的概念

中間件顧名思義,是介於request與response處理之間的一道處理過程,相對比較輕量級,而且在全局上改變django的輸入與輸出。由於改變的是全局,因此須要謹慎實用,用很差會影響到性能。javascript

Django的中間件的定義:css

1
Middleware  is  a framework of hooks into Django’s request / response processing. <br>It’s a light, low - level 「plugin」 system  for  globally altering Django’s  input  or  output.

若是你想修改請求,例如被傳送到view中的HttpRequest對象。 或者你想修改view返回的HttpResponse對象,這些均可以經過中間件來實現。html

可能你還想在view執行以前作一些操做,這種狀況就能夠用 middleware來實現。java

你們可能頻繁在view使用request.user吧。 Django想在每一個view執行以前把user設置爲request的屬性,因而就用了一箇中間件來實現這個目標。因此Django提供了能夠修改request 對象的中間件 AuthenticationMiddlewarepython

Django默認的Middlewaregit

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

每個中間件都有具體的功能。ajax

自定義中間件

中間件中一共有四個方法:正則表達式

process_request

process_view

process_exception

process_response

process_request,process_response

當用戶發起請求的時候會依次通過全部的的中間件,這個時候的請求時process_request,最後到達views的函數中,views函數處理後,在依次穿過中間件,這個時候是process_response,最後返回給請求者。算法

上述截圖中的中間件都是django中的,咱們也能夠本身定義一箇中間件,咱們能夠本身寫一個類,可是必須繼承MiddlewareMixinsql

須要導入

1
from  django.utils.deprecation  import  MiddlewareMixin

 

in views:

def index(request):

    print("view函數...")
    return HttpResponse("OK")

in Mymiddlewares.py:

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class Md1(MiddlewareMixin):

    def process_request(self,request):
        print("Md1請求")
 
    def process_response(self,request,response):
        print("Md1返回")
        return response

class Md2(MiddlewareMixin):

    def process_request(self,request):
        print("Md2請求")
#return HttpResponse("Md2中斷") def process_response(self,request,response): print("Md2返回") return response

結果:

Md1請求
Md2請求
view函數...
Md2返回
Md1返回

注意:若是當請求到達請求2的時候直接不符合條件返回,即return HttpResponse("Md2中斷"),程序將把請求直接發給中間件2返回,而後依次返回到請求者,結果以下:

返回Md2中斷的頁面,後臺打印以下:

Md1請求
Md2請求
Md2返回
Md1返回

流程圖以下:

 

process_view

1
process_view( self , request, callback, callback_args, callback_kwargs)

 Mymiddlewares.py修改以下

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class Md1(MiddlewareMixin):

    def process_request(self,request):
        print("Md1請求")
        #return HttpResponse("Md1中斷")
    def process_response(self,request,response):
        print("Md1返回")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("Md1view")

class Md2(MiddlewareMixin):

    def process_request(self,request):
        print("Md2請求")
        return HttpResponse("Md2中斷")
    def process_response(self,request,response):
        print("Md2返回")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("Md2view")

結果以下:

Md1請求
Md2請求
Md1view
Md2view
view函數...
Md2返回
Md1返回

下圖進行分析上面的過程:

當最後一箇中間的process_request到達路由關係映射以後,返回到中間件1的process_view,而後依次往下,到達views函數,最後經過process_response依次返回到達用戶。

process_view能夠用來調用視圖函數:

class Md1(MiddlewareMixin):

    def process_request(self,request):
        print("Md1請求")
        #return HttpResponse("Md1中斷")
    def process_response(self,request,response):
        print("Md1返回")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):

        # return HttpResponse("hello")

        response=callback(request,*callback_args,**callback_kwargs)
        return response

結果以下:

Md1請求
Md2請求
view函數...
Md2返回
Md1返回

注意:process_view若是有返回值,會越過其餘的process_view以及視圖函數,可是全部的process_response都還會執行。

process_exception

1
process_exception( self , request, exception)

示例修改以下:

class Md1(MiddlewareMixin):

    def process_request(self,request):
        print("Md1請求")
        #return HttpResponse("Md1中斷")
    def process_response(self,request,response):
        print("Md1返回")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):

        # return HttpResponse("hello")

        # response=callback(request,*callback_args,**callback_kwargs)
        # return response
        print("md1 process_view...")

    def process_exception(self):
        print("md1 process_exception...")



class Md2(MiddlewareMixin):

    def process_request(self,request):
        print("Md2請求")
        # return HttpResponse("Md2中斷")
    def process_response(self,request,response):
        print("Md2返回")
        return response
    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("md2 process_view...")

    def process_exception(self):
        print("md1 process_exception...")

結果以下:

Md1請求
Md2請求
md1 process_view...
md2 process_view...
view函數...

Md2返回
Md1返回

流程圖以下:

當views出現錯誤時:

 將md2的process_exception修改以下:

def process_exception(self,request,exception):

        print("md2 process_exception...")
        return HttpResponse("error")

結果以下:

Md1請求
Md2請求
md1 process_view...
md2 process_view...
view函數...
md2 process_exception...
Md2返回
Md1返回

 

Django實現的SESSION

 

一、 基本操做

1
2
3
4
5
6
7
8
1 、設置Sessions值
           request.session[ 'session_name' = "admin"
2 、獲取Sessions值
           session_name  = request.session[ "session_name" ]
3 、刪除Sessions值
           del  request.session[ "session_name" ]
4 、檢測是否操做session值
           if  "session_name"  is  request.session :

一、 基本操做

1
2
3
4
5
6
7
8
1 、設置Sessions值
           request.session[ 'session_name' = "admin"
2 、獲取Sessions值
           session_name  = request.session[ "session_name" ]
3 、刪除Sessions值
           del  request.session[ "session_name" ]
4 、檢測是否操做session值
           if  "session_name"  is  request.session :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
5 、get(key, default = None )
 
fav_color  =  request.session.get( 'fav_color' 'red' )
 
6 、pop(key)
 
fav_color  =  request.session.pop( 'fav_color' )
 
7 、keys()
 
8 、items()
 
9 、setdefault()
 
10 、flush() 刪除當前的會話數據並刪除會話的Cookie。
             這用於確保前面的會話數據不能夠再次被用戶的瀏覽器訪問
             例如,django.contrib.auth.logout() 函數中就會調用它。
 
 
11  用戶session的隨機字符串
         request.session.session_key
  
         # 將全部Session失效日期小於當前日期的數據刪除
         request.session.clear_expired()
  
         # 檢查 用戶session的隨機字符串 在數據庫中是否
         request.session.exists( "session_key" )
  
         # 刪除當前用戶的全部Session數據
         request.session.delete( "session_key" )
  
         request.session.set_expiry(value)
             *  若是value是個整數,session會在些秒數後失效。
             *  若是value是個datatime或timedelta,session就會在這個時間後失效。
             *  若是value是 0 ,用戶關閉瀏覽器session就會失效。
             *  若是value是 None ,session會依賴全局session失效策略。

二、 流程解析圖

 三、 示例

views:

def log_in(request): if request.method=="POST": username=request.POST['user'] password=request.POST['pwd'] user=UserInfo.objects.filter(username=username,password=password) if user: #設置session內部的字典內容 request.session['is_login']='true' request.session['username']=username #登陸成功就將url重定向到後臺的url return redirect('/backend/') #登陸不成功或第一訪問就停留在登陸頁面 return render(request,'login.html') def backend(request): print(request.session,"------cookie") print(request.COOKIES,'-------session') """ 這裏必須用讀取字典的get()方法把is_login的value缺省設置爲False, 當用戶訪問backend這個url先嚐試獲取這個瀏覽器對應的session中的 is_login的值。若是對方登陸成功的話,在login裏就已經把is_login 的值修改成了True,反之這個值就是False的 """ is_login=request.session.get('is_login',False) #若是爲真,就說明用戶是正常登錄的 if is_login: #獲取字典的內容並傳入頁面文件 cookie_content=request.COOKIES session_content=request.session username=request.session['username'] return render(request,'backend.html',locals()) else: """ 若是訪問的時候沒有攜帶正確的session, 就直接被重定向url回login頁面 """ return redirect('/login/') def log_out(request): """ 直接經過request.session['is_login']回去返回的時候, 若是is_login對應的value值不存在會致使程序異常。因此 須要作異常處理 """ try: #刪除is_login對應的value值 del request.session['is_login'] # OR---->request.session.flush() # 刪除django-session表中的對應一行記錄 except KeyError: pass #點擊註銷以後,直接重定向回登陸頁面 return redirect('/login/')

template:

===================================login.html==================
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/login/" method="post"> <p>用戶名: <input type="text" name="user"></p> <p>密碼: <input type="password" name="pwd"></p> <p><input type="submit"></p> </form> </body> </html> ===================================backend.html================== <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>hello {{ username }}</h3> <a href="/logout/">註銷</a> </body> </html>

四、session存儲的相關配置

(1)數據庫配置(默認):

Django默認支持Session,而且默認是將Session數據存儲在數據庫中,即:django_session 表中。

  
a. 配置 settings.py
  
     SESSION_ENGINE  = 'django.contrib.sessions.backends.db'    # 引擎(默認)
      
     SESSION_COOKIE_NAME =  "sessionid"                        # Session的cookie保存在瀏覽器上時的key,即:sessionid=隨機字符串(默認)
     SESSION_COOKIE_PATH =  "/"                                # Session的cookie保存的路徑(默認)
     SESSION_COOKIE_DOMAIN  = None                              # Session的cookie保存的域名(默認)
     SESSION_COOKIE_SECURE  = False                             # 是否Https傳輸cookie(默認)
     SESSION_COOKIE_HTTPONLY  = True                            # 是否Session的cookie只支持http傳輸(默認)
     SESSION_COOKIE_AGE  = 1209600                              # Session的cookie失效日期(2周)(默認)
     SESSION_EXPIRE_AT_BROWSER_CLOSE  = False                   # 是否關閉瀏覽器使得Session過時(默認)
     SESSION_SAVE_EVERY_REQUEST  = False                        # 是否每次請求都保存Session,默認修改以後才保存(默認)

2)緩存配置 

a. 配置 settings.py

  
     SESSION_ENGINE  = 'django.contrib.sessions.backends.cache'   # 引擎
     SESSION_CACHE_ALIAS  = 'default'                             # 使用的緩存別名(默認內存緩存,也能夠是memcache),此處別名依賴緩存的設置
  
  
     SESSION_COOKIE_NAME =  "sessionid"                         # Session的cookie保存在瀏覽器上時的key,即:sessionid=隨機字符串
     SESSION_COOKIE_PATH =  "/"                                 # Session的cookie保存的路徑
     SESSION_COOKIE_DOMAIN  = None                               # Session的cookie保存的域名
     SESSION_COOKIE_SECURE  = False                              # 是否Https傳輸cookie
     SESSION_COOKIE_HTTPONLY  = True                             # 是否Session的cookie只支持http傳輸
     SESSION_COOKIE_AGE  = 1209600                               # Session的cookie失效日期(2周)
     SESSION_EXPIRE_AT_BROWSER_CLOSE  = False                    # 是否關閉瀏覽器使得Session過時
     SESSION_SAVE_EVERY_REQUEST  = False                         # 是否每次請求都保存Session,默認修改以後才保存

(3)文件配置

a. 配置 settings.py

  
     SESSION_ENGINE  = 'django.contrib.sessions.backends.file'     # 引擎
     SESSION_FILE_PATH  = None                                     # 緩存文件路徑,若是爲None,則使用tempfile模塊獲取一個臨時地址tempfile.gettempdir()        
     SESSION_COOKIE_NAME =  "sessionid"                           # Session的cookie保存在瀏覽器上時的key,即:sessionid=隨機字符串
     SESSION_COOKIE_PATH =  "/"                                   # Session的cookie保存的路徑
     SESSION_COOKIE_DOMAIN  = None                                 # Session的cookie保存的域名
     SESSION_COOKIE_SECURE  = False                                # 是否Https傳輸cookie
     SESSION_COOKIE_HTTPONLY  = True                               # 是否Session的cookie只支持http傳輸
     SESSION_COOKIE_AGE  = 1209600                                 # Session的cookie失效日期(2周)
     SESSION_EXPIRE_AT_BROWSER_CLOSE  = False                      # 是否關閉瀏覽器使得Session過時
     SESSION_SAVE_EVERY_REQUEST  = False                           # 是否每次請求都保存Session,默認修改以後才保存

用戶認證 

auth模塊

1
from  django.contrib  import  auth

django.contrib.auth中提供了許多方法,這裏主要介紹其中的三個:

1 、authenticate()   

提供了用戶認證,即驗證用戶名以及密碼是否正確,通常須要username  password兩個關鍵字參數

若是認證信息有效,會返回一個  User  對象。authenticate()會在User 對象上設置一個屬性標識那種認證後端認證了該用戶,且該信息在後面的登陸過程當中是須要的。當咱們試圖登錄一個從數據庫中直接取出來不通過authenticate()的User對象會報錯的!!

1
user  = authenticate(username = 'someone' ,password = 'somepassword' )

2 、login(HttpRequest, user)  

該函數接受一個HttpRequest對象,以及一個認證了的User對象

此函數使用django的session框架給某個已認證的用戶附加上session id等信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
from  django.contrib.auth  import  authenticate, login
   
def  my_view(request):
   username  =  request.POST[ 'username' ]
   password  =  request.POST[ 'password' ]
   user  =  authenticate(username = username, password = password)
   if  user  is  not  None :
     login(request, user)
     # Redirect to a success page.
     ...
   else :
     # Return an 'invalid login' error message.
     ...

3 、logout(request) 註銷用戶  

1
2
3
4
5
from  django.contrib.auth  import  logout
   
def  logout_view(request):
   logout(request)
   # Redirect to a success page.

該函數接受一個HttpRequest對象,無返回值。當調用該函數時,當前請求的session信息會所有清除。該用戶即便沒有登陸,使用該函數也不會報錯。

4 、user對象的 is_authenticated()

要求:

1  用戶登錄後才能訪問某些頁面,

2  若是用戶沒有登陸就訪問該頁面的話直接跳到登陸頁面

3  用戶在跳轉的登錄界面中完成登錄後,自動訪問跳轉到以前訪問的地址

方法1:

1
2
3
def  my_view(request):
   if  not  request.user.is_authenticated():
     return  redirect( '%s?next=%s'  %  (settings.LOGIN_URL, request.path))

方法2:

django已經爲咱們設計好了一個用於此種狀況的裝飾器:login_requierd()

1
2
3
4
5
from  django.contrib.auth.decorators  import login_required
      
@login_required
def  my_view(request):
   ...

若用戶沒有登陸,則會跳轉到django默認的 登陸URL '/accounts/login/ ' (這個值能夠在settings文件中經過LOGIN_URL進行修改)。並傳遞  當前訪問url的絕對路徑 (登錄成功後,會重定向到該路徑)。

User對象

User 對象屬性:username, password(必填項)password用哈希算法保存到數據庫

is_staff : 用戶是否擁有網站的管理權限.

is_active : 是否容許用戶登陸, 設置爲``False``,能夠不用刪除用戶來禁止 用戶登陸

 

2.1 、is_authenticated()

若是是真正的 User 對象,返回值恆爲 True 。 用於檢查用戶是否已經經過了認證。
經過認證並不意味着用戶擁有任何權限,甚至也不檢查該用戶是否處於激活狀態,這只是代表用戶成功的經過了認證。 這個方法很重要, 在後臺用request.user.is_authenticated()判斷用戶是否已經登陸,若是true則能夠向前臺展現request.user.name

2.2 、建立用戶

使用 create_user 輔助函數建立用戶:

1
2
from  django.contrib.auth.models  import  User
user  = User.objects.create_user(username = ' ',password=' ',email=' ')

2.3 、check_password(passwd)

1
用戶須要修改密碼的時候 首先要讓他輸入原來的密碼 ,若是給定的字符串經過了密碼檢查,返回  True

2.4 、修改密碼

使用 set_password() 來修改密碼

1
2
3
user  =  User.objects.get(username = '')
user.set_password(password = '')
user.save 

2.5 、簡單示例

註冊:

def  sign_up(request):
 
     state  =  None
     if  request.method  = =  'POST' :
 
         password  =  request.POST.get( 'password' , '')
         repeat_password  = request.POST.get( 'repeat_password' , '')
         email = request.POST.get( 'email' , '')
         username  =  request.POST.get( 'username' , '')
         if  User.objects. filter (username = username):
                 state  =  'user_exist'
         else :
                 new_user  = User.objects.create_user(username = username, password = password,email = email)
                 new_user.save()
 
                 return  redirect( '/book/' )
     content  =  {
         'state' : state,
         'user' None ,
     }
     return  render(request,  'sign_up.html' , content)  

修改密碼:

@login_required

def  set_password(request):
     user  =  request.user
     state  =  None
     if  request.method  = =  'POST' :
         old_password  =  request.POST.get( 'old_password' , '')
         new_password  =  request.POST.get( 'new_password' , '')
         repeat_password  = request.POST.get( 'repeat_password' , '')
         if  user.check_password(old_password):
             if  not  new_password:
                 state  =  'empty'
             elif  new_password ! =  repeat_password:
                 state  =  'repeat_error'
             else :
                 user.set_password(new_password)
                 user.save()
                 return  redirect( "/log_in/" )
         else :
             state  =  'password_error'
     content  =  {
         'user' : user,
         'state' : state,
     }
     return  render(request,  'set_password.html' , content)

構建一個表單

假設你想在你的網站上建立一個簡單的表單,以得到用戶的名字。你須要相似這樣的模板:

<form action="/your-name/" method="post">

     <label  for = "your_name" >Your  name : </label>
     <input id= "your_name"  type= "text"  name = "your_name" >
     <input type= "submit"  value= "OK" >
</form>

這是一個很是簡單的表單。實際應用中,一個表單可能包含幾十上百個字段,其中大部分須要預填充,並且咱們預料到用戶未來回編輯-提交幾回才能完成操做。

咱們可能須要在表單提交以前,在瀏覽器端做一些驗證。咱們可能想使用很是複雜的字段,以容許用戶作相似從日曆中挑選日期這樣的事情,等等。

這個時候,讓Django 來爲咱們完成大部分工做是很容易的。

so,兩個突出優勢:

    1 form表單提交時,數據出現錯誤,返回的頁面中仍能夠保留以前輸入的數據。

    2 方便地限制字段條件

在Django 中構建一個表單

Form 類

咱們已經計劃好了咱們的 HTML 表單應該呈現的樣子。在Django 中,咱們的起始點是這裏:

#forms.py

 
from  django import forms
 
class NameForm(forms.Form):
     your_name = forms.CharField(label= 'Your name' , max_length=100)

 它定義一個Form 類,只帶有一個字段(your_name)。

字段容許的最大長度經過max_length 定義。它完成兩件事情。首先,它在HTML 的<input> 上放置一個maxlength="100"(這樣瀏覽器將在第一時間阻止用戶輸入多於這個數目的字符)。它還意味着當Django 收到瀏覽器發送過來的表單時,它將驗證數據的長度。

Form 的實例具備一個is_valid() 方法,它爲全部的字段運行驗證的程序。當調用這個方法時,若是全部的字段都包含合法的數據,它將:

  • 返回True
  • 將表單的數據放到cleaned_data屬性中。

完整的表單,第一次渲染時,看上去將像:

<label for="your_name">Your name: </label>

<input id= "your_name"  type= "text"  name = "your_name" maxlength= "100" >

 注意它不包含 <form> 標籤和提交按鈕。咱們必須本身在模板中提供它們。

視圖

發送給Django 網站的表單數據經過一個視圖處理,通常和發佈這個表單的是同一個視圖。這容許咱們重用一些相同的邏輯。

當處理表單時,咱們須要在視圖中實例化它:

#views.py

from django.shortcuts import render
from django.http import HttpResponseRedirect

from .forms import NameForm

def get_name(request):
    # if this is a POST request we need to process the form data
    if request.method == 'POST':
        # create a form instance and populate it with data from the request:
        form = NameForm(request.POST)
        # check whether it's valid:
        if form.is_valid():
            # process the data in form.cleaned_data as required
            # ...
            # redirect to a new URL:
            return HttpResponseRedirect('/thanks/')

    # if a GET (or any other method) we'll create a blank form
    else:
        form = NameForm()

    return render(request, 'name.html', {'form': form})

若是訪問視圖的是一個GET 請求,它將建立一個空的表單實例並將它放置到要渲染的模板的上下文中。這是咱們在第一個訪問該URL 時預期發生的狀況。

若是表單的提交使用POST 請求,那麼視圖將再次建立一個表單實例並使用請求中的數據填充它:form = NameForm(request.POST)。這叫作」綁定數據至表單「(它如今是一個綁定的表單)。

咱們調用表單的is_valid()方法;若是它不爲True,咱們將帶着這個表單返回到模板。這時表單再也不爲空(未綁定),因此HTML 表單將用以前提交的數據填充,而後能夠根據要求編輯並改正它。

若是is_valid()True,咱們將可以在cleaned_data 屬性中找到全部合法的表單數據。在發送HTTP 重定向給瀏覽器告訴它下一步的去向以前,咱們能夠用這個數據來更新數據庫或者作其它處理。

模板

咱們不須要在name.html 模板中作不少工做。最簡單的例子是:

<form action="/your-name/" method="post">

     {% csrf_token %}
     {{ form }}
     <input type= "submit"  value= "Submit"  />
</form>

根據{{ form }},全部的表單字段和它們的屬性將經過Django 的模板語言拆分紅HTML 標記 。

注:Django 原生支持一個簡單易用的跨站請求僞造的防禦。當提交一個啓用CSRF 防禦的POST 表單時,你必須使用上面例子中的csrf_token 模板標籤。

如今咱們有了一個能夠工做的網頁表單,它經過Django Form 描述、經過視圖處理並渲染成一個HTML <form>

 

Django Form 類詳解

綁定的和未綁定的表單實例

綁定的和未綁定的表單 之間的區別很是重要:

  • 未綁定的表單沒有關聯的數據。當渲染給用戶時,它將爲空或包含默認的值。
  • 綁定的表單具備提交的數據,所以能夠用來檢驗數據是否合法。若是渲染一個不合法的綁定的表單,它將包含內聯的錯誤信息,告訴用戶如何糾正數據。

字段詳解

考慮一個比上面的迷你示例更有用的一個表單,咱們完成一個更加有用的註冊表單:

#forms.py

from django import forms

class RegisterForm(forms.Form):
    username = forms.CharField(max_length=100,
                               error_messages={"min_length":"最短爲5個字符","required":"該字段不能爲空"},
                               )
    password = forms.CharField(max_length=100,
                               widget=widgets.PasswordInput(attrs={"placeholder":"password"})
                                )

    telephone=forms.IntegerField(
        error_messages={
            "invalid":"格式錯誤"
        }

                                )


    gender=forms.CharField(
          initial=2,
          widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
             )

    email = forms.EmailField()
    is_married = forms.BooleanField(required=False)

Widgets

每一個表單字段都有一個對應的Widget 類,它對應一個HTML 表單Widget,例如<input type="text">

在大部分狀況下,字段都具備一個合理的默認Widget。例如,默認狀況下,CharField 具備一個TextInput Widget,它在HTML 中生成一個<input type="text">

字段的數據

無論表單提交的是什麼數據,一旦經過調用is_valid() 成功驗證(is_valid() 返回True),驗證後的表單數據將位於form.cleaned_data 字典中。這些數據已經爲你轉換好爲Python 的類型。

注:此時,你依然能夠從request.POST 中直接訪問到未驗證的數據,可是訪問驗證後的數據更好一些。

在上面的聯繫表單示例中,is_married將是一個布爾值。相似地,IntegerField 和FloatField 字段分別將值轉換爲Python 的int 和float

 

使用表單模板

你須要作的就是將表單實例放進模板的上下文。若是你的表單在Context 中叫作form,那麼{{ form }}將正確地渲染它的<label> 和 <input>元素。

表單渲染的選項

對於<label>/<input> 對,還有幾個輸出選項:

  • {{ form.as_table }} 以表格的形式將它們渲染在<tr> 標籤中
  • {{ form.as_p }} 將它們渲染在<p> 標籤中
  • {{ form.as_ul }} 將它們渲染在<li> 標籤中

注意,你必須本身提供<table> 或<ul> 元素。

{{ form.as_p }}會渲染以下:

<form action="">

     < p >
         < label  for="id_username">Username:</ label >
         < input  id="id_username" maxlength="100" name="username" type="text" required="">
     </ p >
 
 
     < p >
         < label  for="id_password">Password:</ label >
         < input  id="id_password" maxlength="100" name="password" placeholder="password" type="password" required="">
     </ p >
 
 
     < p >
         < label  for="id_telephone">Telephone:</ label > < input  id="id_telephone" name="telephone" type="number" required="">
     </ p >
 
 
     < p >
         < label  for="id_email">Email:</ label > < input id="id_email" name="email" type="email" required="">
     </ p >
 
 
     < p >
         < label  for="id_is_married">Is married:</ label > < input  id="id_is_married" name="is_married" type="checkbox">
     </ p >
 
 
     < input  type="submit" value="註冊">
</ form >

手工渲染字段

咱們沒有必要非要讓Django 來分拆表單的字段;若是咱們喜歡,咱們能夠手工來作(例如,這樣容許從新對字段排序)。每一個字段都是表單的一個屬性,可使用{{ form.name_of_field }} 訪問,並將在Django 模板中正確地渲染。例如:

<div class="fieldWrapper">

     {{ form.Username.errors }}
     {{ form.Username.label_tag }}
     {{ form.Username }}
</ div >

渲染表單的錯誤信息

一、

registerForm=RegisterForm(request.POST)

print(type(registerForm.errors))                      #< class  'django.forms.utils.ErrorDict'>
print(type(registerForm.errors["username"]))          #< class  'django.forms.utils.ErrorList'>

二、

使用{{ form.name_of_field.errors }} 顯示錶單錯誤的一個清單,並渲染成一個ul。看上去可能像:

<ul class="errorlist">

     < li >Sender is required.</ li >
</ ul >

form組件的鉤子

def foo(request):


    if request.method=="POST":

        regForm=RegForm(request.POST)

        if regForm.is_valid():
            pass
            # 可用數據: regForm.cleaned_data,
            # 將數據插入數據庫表中


        else:
            pass
            # 可用數據: regForm.errors
            # 能夠利用模板渲染講errors嵌套到頁面中返回
            # 也能夠打包到一個字典中,用於ajax返回

    else:
        regForm=RegForm()
    return render(request,"register.html",{"regForm":regForm})

    

    '''
    實例化時:

        self.fields={
            "username":"字段規則對象",
            "password":"字段規則對象",

        }


    is_valid時:

        self._errors = {}
        self.cleaned_data = {}


        #局部鉤子:

        for name, field in self.fields.items():
              try:

                    value = field.clean(value)
                    self.cleaned_data[name] = value
                    if hasattr(self, 'clean_%s' % name):
                        value = getattr(self, 'clean_%s' % name)()
                        self.cleaned_data[name] = value
              except ValidationError as e:
                    self.add_error(name, e)

        # 全局鉤子:

        self.clean()     # def self.clean():return self.cleaned_data

        return  not self.errors    # True或者False


    '''

form組件補充

一、Django內置字段以下:

Field
    required=True, 是否容許爲空 widget=None, HTML插件 label=None, 用於生成Label標籤或顯示內容 initial=None, 初始值 help_text='', 幫助信息(在標籤旁邊顯示) error_messages=None, 錯誤信息 {'required': '不能爲空', 'invalid': '格式錯誤'} show_hidden_initial=False, 是否在當前插件後面再加一個隱藏的且具備默認值的插件(可用於檢驗兩次輸入是否一直) validators=[], 自定義驗證規則 localize=False, 是否支持本地化 disabled=False, 是否能夠編輯 label_suffix=None Label內容後綴 CharField(Field) max_length=None, 最大長度 min_length=None, 最小長度 strip=True 是否移除用戶輸入空白 IntegerField(Field) max_value=None, 最大值 min_value=None, 最小值 FloatField(IntegerField) ... DecimalField(IntegerField) max_value=None, 最大值 min_value=None, 最小值 max_digits=None, 總長度 decimal_places=None, 小數位長度 BaseTemporalField(Field) input_formats=None 時間格式化 DateField(BaseTemporalField) 格式:2015-09-01 TimeField(BaseTemporalField) 格式:11:12 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12 DurationField(Field) 時間間隔:%d %H:%M:%S.%f ... RegexField(CharField) regex, 自定製正則表達式 max_length=None, 最大長度 min_length=None, 最小長度 error_message=None, 忽略,錯誤信息使用 error_messages={'invalid': '...'} EmailField(CharField) ... FileField(Field) allow_empty_file=False 是否容許空文件 ImageField(FileField) ... 注:須要PIL模塊,pip3 install Pillow 以上兩個字典使用時,須要注意兩點: - form表單中 enctype="multipart/form-data" - view函數中 obj = MyForm(request.POST, request.FILES) URLField(Field) ... BooleanField(Field) ... NullBooleanField(BooleanField) ... ChoiceField(Field) ... choices=(), 選項,如:choices = ((0,'上海'),(1,'北京'),) required=True, 是否必填 widget=None, 插件,默認select插件 label=None, Label內容 initial=None, 初始值 help_text='', 幫助提示 ModelChoiceField(ChoiceField) ... django.forms.models.ModelChoiceField queryset, # 查詢數據庫中的數據 empty_label="---------", # 默認空顯示內容 to_field_name=None, # HTML中value的值對應的字段 limit_choices_to=None # ModelForm中對queryset二次篩選  ModelMultipleChoiceField(ModelChoiceField) ... django.forms.models.ModelMultipleChoiceField TypedChoiceField(ChoiceField) coerce = lambda val: val 對選中的值進行一次轉換 empty_value= '' 空值的默認值 MultipleChoiceField(ChoiceField) ... TypedMultipleChoiceField(MultipleChoiceField) coerce = lambda val: val 對選中的每個值進行一次轉換 empty_value= '' 空值的默認值 ComboField(Field) fields=() 使用多個驗證,以下:即驗證最大長度20,又驗證郵箱格式 fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),]) MultiValueField(Field) PS: 抽象類,子類中能夠實現聚合多個字典去匹配一個值,要配合MultiWidget使用 SplitDateTimeField(MultiValueField) input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y'] input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'] FilePathField(ChoiceField) 文件選項,目錄下文件顯示在頁面中 path, 文件夾路徑 match=None, 正則匹配 recursive=False, 遞歸下面的文件夾 allow_files=True, 容許文件 allow_folders=False, 容許文件夾 required=True, widget=None, label=None, initial=None, help_text='' GenericIPAddressField protocol='both', both,ipv4,ipv6支持的IP格式 unpack_ipv4=False 解析ipv4地址,若是是::ffff:192.0.2.1時候,可解析爲192.0.2.1, PS:protocol必須爲both才能啓用 SlugField(CharField) 數字,字母,下劃線,減號(連字符) ... UUIDField(CharField) uuid類型 ...

二、Django內置插件:

TextInput(Input)
NumberInput(TextInput)
EmailInput(TextInput)
URLInput(TextInput)
PasswordInput(TextInput)
HiddenInput(TextInput)
Textarea(Widget)
DateInput(DateTimeBaseInput)
DateTimeInput(DateTimeBaseInput)
TimeInput(DateTimeBaseInput)
CheckboxInput
Select
NullBooleanSelect
SelectMultiple
RadioSelect
CheckboxSelectMultiple
FileInput
ClearableFileInput
MultipleHiddenInput
SplitDateTimeWidget
SplitHiddenDateTimeWidget
SelectDateWidget

三、經常使用選擇插件:

# 單radio,值爲字符串
# user = fields.CharField(
#     initial=2,
#     widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
# )
 
# 單radio,值爲字符串
# user = fields.ChoiceField(
#     choices=((1, '上海'), (2, '北京'),),
#     initial=2,
#     widget=widgets.RadioSelect
# )
 
# 單select,值爲字符串
# user = fields.CharField(
#     initial=2,
#     widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
# )
 
# 單select,值爲字符串
# user = fields.ChoiceField(
#     choices=((1, '上海'), (2, '北京'),),
#     initial=2,
#     widget=widgets.Select
# )
 
# 多選select,值爲列表
# user = fields.MultipleChoiceField(
#     choices=((1,'上海'),(2,'北京'),),
#     initial=[1,],
#     widget=widgets.SelectMultiple
# )
 
 
# 單checkbox
# user = fields.CharField(
#     widget=widgets.CheckboxInput()
# )
 
 
# 多選checkbox,值爲列表
# user = fields.MultipleChoiceField(
#     initial=[2, ],
#     choices=((1, '上海'), (2, '北京'),),
#     widget=widgets.CheckboxSelectMultiple
# )

引入:

https://www.cnblogs.com/wupeiqi/articles/6144178.html

相關文章
相關標籤/搜索