Django基礎三之視圖函數

Django基礎三之視圖函數

一 Django的視圖函數view

一個視圖函數(類),簡稱視圖,是一個簡單的Python 函數(類),它接受Web請求而且返回Web響應。php

響應能夠是一張網頁的HTML內容,一個重定向,一個404錯誤,一個XML文檔,或者一張圖片。html

不管視圖自己包含什麼邏輯,都要返回響應。代碼寫在哪裏也無所謂,只要它在你當前項目目錄下面。除此以外沒有更多的要求了——能夠說「沒有什麼神奇的地方」。爲了將代碼放在某處,你們約定成俗將視圖放置在項目(project)或應用程序(app)目錄中的名爲views.py的文件中。python

一個簡單的視圖

  下面是一個以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

  • 首先,咱們從 django.http模塊導入了HttpResponse類,以及Python的datetime庫。編程

  • 接着,咱們定義了current_datetime函數。它就是視圖函數。每一個視圖函數都使用HttpRequest對象做爲第一個參數,而且一般稱之爲request瀏覽器

    注意,視圖函數的名稱並不重要;不須要用一個統一的命名方式來命名,以便讓Django識別它。咱們將其命名爲current_datetime,是由於這個名稱可以比較準確地反映出它實現的功能。服務器

  • 這個視圖會返回一個HttpResponse對象,其中包含生成的響應。每一個視圖函數都負責返回一個HttpResponse對象。app

  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的用法!
相關文章
相關標籤/搜索