1. 程式人生 > >Django基礎三之檢視函式

Django基礎三之檢視函式

Django基礎三之檢視函式

一 Django的檢視函式view

一個檢視函式(類),簡稱檢視,是一個簡單的Python 函式(類),它接受Web請求並且返回Web響應。

響應可以是一張網頁的HTML內容,一個重定向,一個404錯誤,一個XML文件,或者一張圖片。

無論檢視本身包含什麼邏輯,都要返回響應。程式碼寫在哪裡也無所謂,只要它在你當前專案目錄下面。除此之外沒有更多的要求了——可以說“沒有什麼神奇的地方”。為了將程式碼放在某處,大家約定成俗將檢視放置在專案(project)或應用程式(app)目錄中的名為views.py的檔案中。

一個簡單的檢視

  下面是一個以HTML文件的形式返回當前日期和時間的檢視:

from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)

讓我們來逐行解釋下上面的程式碼:

  • 首先,我們從 django.http模組匯入了HttpResponse類,以及Python的datetime

    庫。

  • 接著,我們定義了current_datetime函式。它就是檢視函式。每個檢視函式都使用HttpRequest物件作為第一個引數,並且通常稱之為request

    注意,檢視函式的名稱並不重要;不需要用一個統一的命名方式來命名,以便讓Django識別它。我們將其命名為current_datetime,是因為這個名稱能夠比較準確地反映出它實現的功能。

  • 這個檢視會返回一個HttpResponse物件,其中包含生成的響應。每個檢視函式都負責返回一個HttpResponse物件。

  Django使用請求和響應物件來通過系統傳遞狀態。

  當瀏覽器向服務端請求一個頁面時,Django建立一個HttpRequest物件,該物件包含關於請求的元資料。然後,Django載入相應的檢視,將這個HttpRequest物件作為第一個引數傳遞給檢視函式。

  每個檢視負責返回一個HttpResponse物件。檢視層,熟練掌握兩個物件即可:請求物件(request)和響應物件(HttpResponse)

二 CBV和FBV

FBV(function base views) 就是在視圖裡使用函式處理請求。

    之前都是FBV模式寫的程式碼,所以就不寫例子了。

CBV(class base views) 就是在視圖裡使用類處理請求。

  Python是一個面向物件的程式語言,如果只用函式來開發,有很多面向物件的優點就錯失了(繼承、封裝、多型)。所以Django在後來加入了Class-Based-View。可以讓我們用類寫View。這樣做的優點主要下面兩種:

  1. 提高了程式碼的複用性,可以使用面嚮物件的技術,比如Mixin(多繼承)
  2. 可以用不同的函式針對不同的HTTP方法處理,而不是通過很多if判斷,提高程式碼可讀性

如果我們要寫一個處理GET方法的view,用函式寫的話是下面這樣。

def login(request):
   if request.method == 'GET':
      return render(request, 'login.html')

如果用class-based view寫的話,就是下面這樣:

#類寫法:
class LoginView(View):
   def get(self,request):
      print('get方法執行了')
      return render(request,'login2.html')
    def post(self,request):
        username=request.POST.get('username')
        password=request.POST.get('password')
        print('post方法執行了')
        print(username,password)
        return HttpResponse('cg')

Django的url是將一個請求分配給可呼叫的函式的,而不是一個class。針對這個問題,class-based view提供了一個as_view()靜態方法(也就是類方法),呼叫這個方法,會建立一個類的例項,然後通過例項呼叫dispatch()方法,dispatch()方法會根據request的method的不同調用相應的方法來處理request(如get(),post()等)。到這裡,這些方法和function-based view差不多了,要接收request,得到一個response返回。如果方法沒有定義,會丟擲HttpResponseNotAllowed異常。

    注意:使用CBV時,urls.py中也做對應的修改::

from django.conf.urls import url,include
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login2/$', LoginView.as_view()),

]

 CBV傳參,和FBV類似,有名分組,無名分組

    url寫法:無名分組的

 url(r'^cv/(\d{2})/', views.Myd.as_view(),name='cv'),
 url(r'^cv/(?P<n>\d{2})/', views.Myd.as_view(name='xxx'),name='cv'),#如果想給類的name屬性賦值,前提你的Myd類裡面必須有name屬性(類屬性,定義init方法來接受屬性行不通,但是可以自行研究一下,看看如何行通,意義不大),並且之前類裡面的name屬性的值會被覆蓋掉

三 使用Mixin(瞭解)

我覺得要理解django的class-based-view(以下簡稱cbv),首先要明白django引入cbv的目的是什麼。在django1.3之前,generic view也就是所謂的通用檢視,使用的是function-based-view(fbv),亦即基於函式的檢視。有人認為fbv比cbv更pythonic,竊以為不然。python的一大重要的特性就是面向物件。而cbv更能體現python的面向物件。cbv是通過class的方式來實現檢視方法的。class相對於function,更能利用多型的特定,因此更容易從巨集觀層面上將專案內的比較通用的功能抽象出來。關於多型,不多解釋,有興趣的同學自己Google。總之可以理解為一個東西具有多種形態(的特性)。cbv的實現原理通過看django的原始碼就很容易明白,大體就是由url路由到這個cbv之後,通過cbv內部的dispatch方法進行分發,將get請求分發給cbv.get方法處理,將post請求分發給cbv.post方法處理,其他方法類似。怎麼利用多型呢?cbv裡引入了mixin的概念。Mixin就是寫好了的一些基礎類,然後通過不同的Mixin組合成為最終想要的類。

  所以,理解cbv的基礎是,理解Mixin。Django中使用Mixin來重用程式碼,一個View Class可以繼承多個Mixin,但是隻能繼承一個View(包括View的子類),推薦把View寫在最右邊,多個Mixin寫在左邊。

四 給檢視加裝飾器

使用裝飾器裝飾FBV

    FBV本身就是一個函式,所以和給普通的函式加裝飾器無差:

from django.shortcuts import render, HttpResponse, redirect
def wrapper(f):
   def innser(*args, **kwargs):
      print('執行前')
      ret = f(*args, **kwargs)
      print('執行後')
      return ret
   return innser

@wrapper
def index(request):
   print('12321')
   return render(request, 'index.html')

使用裝飾器裝飾CBV

    類中的方法與獨立函式不完全相同,因此不能直接將函式裝飾器應用於類中的方法 ,我們需要先將其轉換為方法裝飾器。

    Django中提供了method_decorator裝飾器用於將函式裝飾器轉換為方法裝飾器。

def wrapper(f):
   def innser(*args, **kwargs):
      print('執行前')
      ret = f(*args, **kwargs)
      print('執行後')
      return ret

   return innser

from django.views import View
from django.utils.decorators import method_decorator
@method_decorator(wrapper,name='get')
class LoginView(View):
    # 使用CBV時要注意,請求過來後會先執行dispatch()這個方法,如果需要批量對具體的請求處理方法,如get,post等做一些操作的時候,這裡我們可以手動改寫dispatch方法,這個dispatch方法就和在FBV上加裝飾器的效果一樣。
    # @method_decorator(wrapper)
    def dispatch(self, request, *args, **kwargs):
        # print('之前')
        ret=super().dispatch(request, *args, **kwargs)
        # print('之後')
        return ret

    # @method_decorator(wrapper)
    def get(self,request):
        print('get方法執行了')
        return render(request,'login2.html')

    # @method_decorator(wrapper)
    def post(self,request):
        username=request.POST.get('username')
        password=request.POST.get('password')
        print('post方法執行了')
        print(username,password)
        return HttpResponse('cg')

另外給cbv新增裝飾器的時候(先作為了解):

  • 直接新增在dispatch裡面,這樣每個函式都會執行

    from django.utils.decorators import method_decorator
    
    @method_decorator(login_test)
    def dispatch(self, request, *args, **kwargs):
    res = super(IndexView, self).dispatch(request, *args, **kwargs)
    return res
  • 新增在每一個函式中

    from django.utils.decorators import method_decorator
    
    @method_decorator(login_test)
    def get(self, request, *args, **kwargs):
    return render(request, 'index.html')
  • 直接新增在類上,後面的name表示只給get新增裝飾器

    from django.utils.decorators import method_decorator
    
    @method_decorator(login_test, name='get')

    get是給get方法加 (以這種方式如果想給多個方法加裝飾器,需要寫多層裝飾器,因為name這個引數的值必須是個字串,並且不能同時寫兩個方法)

     @method_decorator(login_test, name='post')  post是給post方法加
         class IndexView(View):
        def get(self,request):
          pass
  • 新增裝飾器前必須匯入from django.utils.decorators import method_decorator

  • 新增裝飾器的格式必須為@method_decorator(),括號裡面為裝飾器的函式名

  • 給類新增是必須宣告name

  • 注意csrf-token裝飾器的特殊性,在CBV模式下它只能加在dispatch上面(後面再說)

  下面這是csrf_token的裝飾器:

  @csrf_protect,為當前函式強制設定防跨站請求偽造功能,即便settings中沒有設定csrfToken全域性中介軟體。

  @csrf_exempt,取消當前函式防跨站請求偽造功能,即便settings中設定了全域性中介軟體。

  注意:from django.views.decorators.csrf import csrf_exempt,csrf_protect

五 request物件官方文件

請求相關的屬性方法(request--HttpRequest物件)

def index(request): #http相關請求資訊---封裝--HttpRequest物件

    if request.method == 'GET':
        print(request.body)  #獲取post請求提交過來的原始資料
        print(request.GET)   #獲取GET請求提交的資料
        # print(request.META)  # 請求頭相關資訊,就是一個大字典
        print(request.path) #/index/ 路徑
        print(request.path_info) #/index/ 路徑
        print(request.get_full_path())  #/index/?username=dazhuang&password=123
        
        return render(request,'index.html')
    else:
        print(request.body)  # b'username=dazhuang'
        print(request.POST) #獲取POST請求提交的資料
        return HttpResponse('男賓三位,拿好手牌!')

六 response物件

與由Django自動建立的HttpRequest物件相比,HttpResponse物件是我們的職責範圍了。我們寫的每個檢視都需要例項化,填充和返回一個HttpResponse。

  HttpResponse類位於django.http模組中。

HttpResponse  --- 回覆字串的時候來使用
render --- 回覆一個html頁面的時候使用
redirect -- 重定向
    示例:
    def login(request):
        if request.method == 'GET':
            return render(request,'login.html')
        else:
            username = request.POST.get('username')
            password = request.POST.get('password')
            if username == 'taibai' and password == 'dsb':
                # return render(request,'home.html')
                return  redirect('/home/')  #重定向
            else:
                return HttpResponse('滾犢子,趕緊去充錢!!!')

    #首頁
    def home(request):
        return render(request,'home.html')
    
    
HttpResponse.content:響應內容
HttpResponse.charset:響應內容的編碼
HttpResponse.status_code:響應的狀態碼

redirect() :給瀏覽器了一個30x的狀態碼

  引數可以是:

  1. 一個模型:將呼叫模型的get_absolute_url() 函式

   2.一個檢視,可以帶有引數:將使用urlresolvers.reverse 來反向解析名稱

   3.一個絕對的或相對的URL,將原封不動的作為重定向的位置。

   預設返回一個臨時的重定向;傳遞permanent=True 可以返回一個永久的重定向。

   示例:

​ 你可以用多種方式使用redirect() 函式。

def login(request):
   if request.method == 'GET':
      return render(request, 'login.html')
   else:
      username = request.POST.get('username')
      password = request.POST.get('password')
      if username == 'anwen@123' and password == '123':
         return redirect('/app01/home/')  ##重定向到/app01/home/路徑,這也是傳送了一個請求,別忘了在上面引入這個redirect類,和render、Httpresponse在一個地方引入
      else:
         return HttpResponse('登入失敗!')


def home(request):
   return render(request, 'home.html')
#app01裡的 urls.py
from django.conf.urls import url
from app01 import views
urlpatterns=[
   url(r'^$',views.login),
   url(r'^home/',views.home),
]

上面幾個檔案搞好之後,我們重啟Django專案,然後登陸頁面的輸入網址,注意,你輸入的網址埠要和你啟動的django專案的埠一樣。

| 一點選提交按鈕,你看一下network裡面傳送了幾個請求:兩個請求,一個是login請求,一個index請求。 |

擴充套件閱讀:** key兩次請求,關於301和302:   

1)301和302的區別。
  301和302狀態碼都表示重定向,就是說瀏覽器在拿到伺服器返回的這個狀態碼後會自動跳轉到一個新的URL地址,這個地址可以從響應的Location首部中獲取
  (使用者看到的效果就是他輸入的地址A瞬間變成了另一個地址B)——這是它們的共同點。

  他們的不同在於。301表示舊地址A的資源已經被永久地移除了(這個資源不可訪問了),搜尋引擎在抓取新內容的同時也將舊的網址交換為重定向之後的網址;

  302表示舊地址A的資源還在(仍然可以訪問),這個重定向只是臨時地從舊地址A跳轉到地址B,搜尋引擎會抓取新的內容而儲存舊的網址。 SEO302好於301

2)重定向原因:
(1)網站調整(如改變網頁目錄結構);
(2)網頁被移到一個新地址;
(3)網頁副檔名改變(如應用需要把.php改成.Html或.shtml)。
        這種情況下,如果不做重定向,則使用者收藏夾或搜尋引擎資料庫中舊地址只能讓訪問客戶得到一個404頁面錯誤資訊,訪問流量白白喪失;再者某些註冊了多個域名的
    網站,也需要通過重定向讓訪問這些域名的使用者自動跳轉到主站點等。
    
    
    臨時重定向(響應狀態碼:302)和永久重定向(響應狀態碼:301)對普通使用者來說是沒什麼區別的,它主要面向的是搜尋引擎的機器人。
  A頁面臨時重定向到B頁面,那搜尋引擎收錄的就是A頁面。
  A頁面永久重定向到B頁面,那搜尋引擎收錄的就是B頁面。
  用redirect可以解釋APPEND_SLASH的用法!