【Django】視圖系統

@html

 


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

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

不管視圖自己包含什麼邏輯,都要返回響應。代碼寫在哪裏也無所謂,只要它在你當前項目目錄下面。除此以外沒有更多的要求了,能夠說「沒什麼神奇的地方」。django

爲了將代碼存放位置統一,你們約定成俗將視圖放置在項目(project)或應用程序(app)目錄中的名爲views.py的文件中。json

一個簡單的視圖:瀏覽器

# 以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(now)

讓咱們來逐行解釋上面的代碼:緩存

  1. 首先,咱們從Django.http模塊中導入了HttpResponse類,以及Python的datetime庫.
  2. 接着,咱們定義了current_datetime函數,這個函數就稱爲視圖函數,每一個視圖函數都適用HttpRequest對象做爲第一個參數,而且一般稱之爲request.
  3. 這個視圖會返回一個Httpresponse對象,其中包含生成的響應。每一個視圖函數都負責返回一個HttpResponse對象.

Django使用請求和響應對象經過系統傳遞狀態.
.
當瀏覽器向服務器請求一個頁面時,Django建立一個HttpRequest對象,該對象包含關於請求的元數據。而後,Django加載相應的視圖,將這個HttpRequest對象做爲第一個參數傳遞給視圖函數.
.
每一個視圖負責返回一個HttpResponse對象.安全



FBV 與 CBV

FBV (function based view) 基於函數的視圖.
CBV (class based view) 基於類的視圖.服務器

對於相同的邏輯,FBV與CBV的寫法:
FBVcookie

from django.shortcuts import render, redirect

def classes_add(request):
    if request.method == 'POST':
        add_classes_name = request.POST.get('add_classes_name')  # 提取班級名稱
        Classes.objects.create(name=add_classes_name)  # 將新建班級寫入數據庫
        return redirect('/classes/')  # 跳轉至展現頁面
    return render(request, 'classes_add.html')  # 返回添加頁面

# urls文件
url(r'^classes_add/$', views.classes_add),

CBV

from django.shortcuts import render, redirect
from django.views import View

class ClassesAdd(View):
    # 可自定義容許的請求
    # http_method_names = ['get', 'post']

    # 處理get請求的邏輯
    def get(self, request):
        return render(request, 'classes_add.html')  # 返回添加頁面

    # 處理post請求的邏輯
    def post(self, request):
        add_classes_name = request.POST.get('add_classes_name')  # 提取班級名稱
        Classes.objects.create(name=add_classes_name)  # 將新建班級寫入數據庫
        return redirect('/classes/')  # 跳轉至展現頁面

# urls文件
url(r'^classes_add/$', views.ClassesAdd.as_view()),


給視圖加裝飾器

FBV自己就是函數,因此和給普通函數加裝飾器無差別.

使用裝飾器裝飾CBV

類中的方法與獨立函數不徹底相同,所以不能直接將函數裝飾器應用於類中的方法,咱們須要將其轉換爲方法裝飾器.
Django中提供了method_decorator裝飾器,用於將函數裝飾器轉換爲方法裝飾器.

裝飾器函數以下:

import time

def wrapper(fn):
    def inner(*args, **kwargs):
        start_time = time.time()
        ret = fn(*args, **kwargs)
        end_time = time.time()
        print("used:", end_time - start_time)
        return ret
    return inner

裝飾CBV寫法以下:

from django.shortcuts import render, redirect
from django.views import View
# 導入method_decorator
from django.utils.decorators import method_decorator

class ClassesAdd(View):

    # 處理get請求的邏輯
    @method_decorator(wrapper)  #!
    def get(self, request):
        return render(request, 'classes_add.html')  # 返回添加頁面

    # 處理post請求的邏輯
    @method_decorator(wrapper)  #!
    def post(self, request):
        add_classes_name = request.POST.get('add_classes_name')  # 提取班級名稱
        Classes.objects.create(name=add_classes_name)  # 將新建班級寫入數據庫
        return redirect('/classes/')  # 跳轉至展現頁面

關於dispatch()方法

使用CBV時需注意,請求過來後會先執行dispatch()方法,若是須要批量對具體的請求處理,如get、post等作一些操做的時,咱們能夠在子類中手動改寫此方法,這種操做其實和在FBV上加裝飾器的效果同樣.

from django.shortcuts import render, redirect
from django.views import View

class ClassesAdd(View):

    # 可自定義處理請求先後的邏輯
    def dispatch(self, request, *args, **kwargs):
        # 處理請求以前的邏輯
        ret = super().dispatch(request, *args, **kwargs)
        # 處理請求以後的邏輯
        return ret

    # 處理get請求的邏輯
    def get(self, request):
        return render(request, 'classes_add.html')  # 返回添加頁面

    # 處理post請求的邏輯
    def post(self, request):
        add_classes_name = request.POST.get('add_classes_name')  # 提取班級名稱
        Classes.objects.create(name=add_classes_name)  # 將新建班級寫入數據庫
        return redirect('/classes/')  # 跳轉至展現頁面


Request對象 與 Response對象

request對象

當一個頁面被請求時,Django會建立一個包含本次請求原消息的HttpRequest對象.
Django會將這個對象自動傳遞給相應的視圖函數,通常視圖函數約定俗成使用request參數承接這個對象.
官方文檔


請求相關的屬性

屬性 解釋
path_info 返回用戶訪問的url,不包含域名及?後面的內容
method 請求中使用的HTTP方法的字符串表示,全大寫表示
GET 包含全部HTTP GET參數的類字典對象
POST 包含全部HTTP POST參數的類字典對象
body 請求體,byte類型,request.POST的數據就是從body裏面提取到的

其它屬性

全部的屬性都應該被認爲是隻讀的,除非另有說明.

Django將請求報文中的請求行、頭部信息、內容主題封裝成HttpRequest類中的屬性.
除了特殊說明的以外,其它均爲只讀的.
.
scheme
表示請求方案的字符串(一般爲http或https)
.
body
一個字符串,表明請求報文的主體。在處理非HTTP形式的報文時很是有用,如:二進制文件、XML、Json等.
但若是要處理表單數據,仍是推薦是POST.
另外,咱們還可使用Python的類文件方法去操做它,詳細參考request.read()方法.
.
path
一個字符串,表示請求的路徑組件(不含域名及?後面的內容).
.
encoding
一個字符串,表示提交的數據的編碼方式.
若是爲None,則表示是使用DEFAULT_CHARSET的設置,默認爲"utf-8".
此屬性是可寫的,能夠經過修改它來改變訪問表單數據使用的編碼,修改後對屬性的任何訪問都將使用新的encoding值(例如從GET或POST中讀取數據).
若是肯定表單數據的編碼不是DEFAULT_CHARSET,則使用它.
.
POST
一個相似於字典的對象,若是請求中包含表單數據,則將這些數據封裝成QueryDict對象.
POST請求能夠帶有空的POST字典 —— 經過HTTP POST方法發送一個沒有任何數據的表單,也依然會建立QueryDict對象.
所以,咱們使用if request.method == 'POST':來判斷使用的是不是POST方法,而不是使用if request.method:
另外,若是使用POST上傳文件的話,文件信息將包含在FILES屬性中.
.
COOKIES
一個標準的Python字典,包含全部的cookie,鍵和值都爲字符串.
.
FILES
一個相似於字典的對象,包含全部的上傳文件信息.
FILES中的每一個鍵爲< input type="file" name="" />中name,值則爲對應的數據.
注意,FILES只有在請求的方法爲POST且提交的< form>表單中帶有enctype="multipart/form-data"的狀況下才會包含數據。不然,FILES將爲一個空的相似於字典的對象.
.
session
一個便可讀又可寫的相似於字典的對象,表示當前的會話.
只有當Django啓用會話的支持時纔可用.
完整的細節請參見關於會話的文檔.

META
一個標準的Python字典,包含全部的HTTP首部.
具體的頭部信息取決於客戶端和服務器.
.
CONTENT_LENGTH —— 請求的正文的長度(是一個字符串)。
CONTENT_TYPE —— 請求的正文的MIME 類型。
HTTP_ACCEPT —— 響應可接收的Content-Type。
HTTP_ACCEPT_ENCODING —— 響應可接收的編碼。
HTTP_ACCEPT_LANGUAGE —— 響應可接收的語言。
HTTP_HOST —— 客服端發送的HTTP Host 頭部。
HTTP_REFERER —— Referring 頁面。
HTTP_USER_AGENT —— 客戶端的user-agent 字符串。
QUERY_STRING —— 單個字符串形式的查詢字符串(未解析過的形式)。
REMOTE_ADDR —— 客戶端的IP 地址。
REMOTE_HOST —— 客戶端的主機名。
REMOTE_USER —— 服務器認證後的用戶。
REQUEST_METHOD —— 一個字符串,例如"GET" 或"POST"。
SERVER_NAME —— 服務器的主機名。
SERVER_PORT —— 服務器的端口(是一個字符串)。
.
從上面能夠看到,除 CONTENT_LENGTH 和 CONTENT_TYPE 以外,請求中的任何 HTTP 首部轉換爲 META 的鍵時,
都會將全部字母大寫並將鏈接符替換爲下劃線最後加上 HTTP_ 前綴。
因此,一個叫作 X-Bender 的頭部將轉換成 META 中的 HTTP_X_BENDER 鍵。

user
一個 AUTH_USER_MODEL 類型的對象,表示當前登錄的用戶.
若是當前沒有用戶登錄,則user將設置爲 django.contrib.auth.models.AnonymousUser 的一個實例.
能夠經過 is_authenticated() 來區分它們.
例:

if request.user.is_authenticated():
    # Do something for logged-in users.
else:
    # Do something for anonymous users.

user 只用當Django啓用 AuthenticationMiddleware 中間件時纔可用.
.
匿名用戶: classes models.AnonymousUser
django.contrib.auth.models.AnonymousUser 類實現了Django.contrib.auth.models.User 接口,但具備下面幾個不一樣點:
.
id 永遠爲None.
username 永遠爲空字符串.
get_username() 永遠返回空字符串.
is_staff 與 is_superuser 永遠爲False.
is_active 永遠爲 False.
groups 與 user_permissions 永遠爲空.
is_anonymous() 返回True 而不是False.
is_authenticated() 返回False 而不是True.
set_password()、check_password()、save()、delete() 都將引起 NotImplementedError 錯誤
New in Django 1.8:
新增 AnonymousUser.get_username() 以更好地模擬 django.contrib.auth.models.User。

上傳文件實例:

def upload(request):
    if request.method == 'POST':
        file_obj = request.FILES.get('file')  # 獲取文件對象(獲取文件信息要用FILES)
        upload_file_path = os.path.join(settings.BASE_DIR, 'upload_file', file_obj.name)  # 文件上傳路徑, file_obj.name爲str類型
        # 判斷文件是否存在的邏輯
        if os.path.exists(upload_file_path):
            name, suffix = file_obj.name.split('.')  # 切割文件名與文件後綴
            file_name = name + "_文件副本1." + suffix  # 從新拼接文件名
            upload_file_path = os.path.join(settings.BASE_DIR, 'upload_file', file_name)  # 文件上傳路徑
            # 判斷文件是否有副本的邏輯
            if os.path.exists(upload_file_path):
                file_list = os.listdir(os.path.dirname(upload_file_path))
                # 獲取全部副本文件
                file_copy_num = []
                for i in file_list:
                    if name not in i: continue
                    copy_num = i.split('.')[0][-1]  # 提取副本數字
                    if copy_num == name[-1]: continue
                    file_copy_num.append(copy_num)
                file_copy_max = int(max(file_copy_num))  # 提取最大的副本數字
                file_name = name + "_文件副本" + str(file_copy_max + 1) + '.' + suffix
                upload_file_path = os.path.join(settings.BASE_DIR, 'upload_file', file_name)  # 文件上傳路徑
        with open(upload_file_path, 'wb') as f:
            [f.write(chunk) for chunk in file_obj.chunks()]  # 從文件裏一點一點的讀數據,寫到文件裏
        return HttpResponse("文件上傳成功!")  # 返回提示信息
    return render(request, 'upload.html')  # 返回上傳頁面

方法

get_host
.
根據從HTTP_X_FORWARDED_HOST(若是打開 USE_X_FORWARDED_HOST,默認爲False)和 HTTP_HOST 頭部信息返回請求的原始主機。
.
若是這兩個頭部沒有提供相應的值,則使用SERVER_NAME 和SERVER_PORT,在PEP 3333 中有詳細描述。
.
USE_X_FORWARDED_HOST:一個布爾值,用於指定是否優先使用 X-Forwarded-Host 首部,僅在代理設置了該首部的狀況下,才能夠被使用。
.
例如:127.0.0.1:8000
.
注意:當主機位於多個代理後面時,get_host() 方法將會失敗。除非使用中間件重寫代理的首部。

get_full_path
.
返回path,若是能夠 將加上查詢字符串.
例如:/list/test/?name=zyk

get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
.
返回簽名過的Cookie 對應的值,若是簽名再也不合法則返回django.core.signing.BadSignature。
.
若是提供 default 參數,將不會引起異常並返回 default 的值。
.
可選參數salt 能夠用來對安全密鑰強力***提供額外的保護。max_age 參數用於檢查Cookie 對應的時間戳以確保Cookie 的時間不會超過max_age 秒。

>>> request.get_signed_cookie('name')
       'Tony'
>>> request.get_signed_cookie('name', salt='name-salt')
       'Tony' # 假設在設置cookie的時候使用的是相同的salt
>>> request.get_signed_cookie('non-existing-cookie')
        ...
        KeyError: 'non-existing-cookie'    # 沒有相應的鍵時觸發異常
>>> request.get_signed_cookie('non-existing-cookie', False)
        False
>>> request.get_signed_cookie('cookie-that-was-tampered-with')
        ...
        BadSignature: ...   
>>> request.get_signed_cookie('name', max_age=60)
...
SignatureExpired: Signature age 1677.3839159 > 60 seconds
>>> request.get_signed_cookie('name', False, max_age=60)
        False

is_secure()
.
若是請求時安全的,則返回True,即請求時經過HTTPS發起的.

is_ajax
.
若是請求是經過XMLHttpRequest 發起的,則返回True,方法是檢查 HTTP_X_REQUESTED_WITH 相應的首部是不是字符串'XMLHttpRequest'。
.
大部分現代的 JavaScript 庫都會發送這個頭部。若是你編寫本身的 XMLHttpRequest 調用(在瀏覽器端),你必須手工設置這個值來讓 is_ajax() 能夠工做。
.
若是一個響應須要根據請求是不是經過AJAX 發起的,而且你正在使用某種形式的緩存例如Django 的 cache middleware,你應該使用 vary_on_headers('HTTP_X_REQUESTED_WITH') 裝飾你的視圖以讓響應可以正確地緩存。

注意:鍵值對中的值爲多個時(例如checkbox類型的input標籤),因該使用getlist()方法.

response對象

與Django自動建立的HttpRequest對象相比,HttpResponse對象是咱們的職責範圍了,咱們寫的每一個視圖都須要實例化,填充和返回一個HttpResponse.
HttpResponse類 位於django.http模塊中.

傳遞字符串:

from django.http import HttpResponse
response = HttpResponse("Here's the text of the Web page.")
response = HttpResponse("Text only, please.", content_type="text/plain")

設置或刪除響應頭消息:

response = HttpResponse()
response['Content-Type'] = 'text/html; charset=UTF-8'
del response['Content-Type']

屬性

  • HttpResponse.content:響應內容
  • HttpResponse.charset:響應內容的編碼
  • HttpResponse.status_code:響應的狀態嗎


JsonResponse對象

JsonResponse是HttpResponse的子類,專門用來生成JSON編碼的響應.

from django.http import JsonResponse

def test(request):
    dct = {'name': 'zyk', 'sex': 'boy'}
    response = JsonResponse(dct)
    print(response.content)
    return response

默認只會傳遞字典類型的數據,若是要傳遞非字典類型的數據,須要設置參數safe=False:

response = JsonResponse([1, 2, 3], safe=False)
相關文章
相關標籤/搜索