1. 程式人生 > >Django 系列博客(七)

Django 系列博客(七)

精確 封裝 session tip 兩種 lec 上傳 rmi Language

Django 系列博客(七)

前言

本篇博客介紹 Django 中的視圖層中的相關參數,HttpRequest 對象、HttpResponse 對象、JsonResponse,以及視圖層的兩種響應方式 CBV 和 FBV,還有簡單的文件上傳。

視圖函數

一個視圖函數,簡稱視圖,是一個簡單的Python 函數,它接受Web請求並且返回Web響應。響應可以是一張網頁的HTML內容,一個重定向,一個404錯誤,一個XML文檔,或者一張圖片. . . 是任何東西都可以。無論視圖本身包含什麽邏輯,都要返回響應。代碼寫在哪裏也無所謂,只要它在你的Python目錄下面。除此之外沒有更多的要求了——可以說“沒有什麽神奇的地方”。為了將代碼放在某處,約定是將視圖放置在項目或應用程序目錄中的名為views.py

的文件中。

下面是一個返回當前日期和時間作為 HTML 文檔的視圖:

from django.shortcuts import render, HttpResponse, HttpResponseRedirect, redirect
import datetime

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

這段代碼解析:

  • django.shortcuts模塊導入了HttpResponse類,以及Python的datetime庫;
  • 定義了current_datetime函數。它就是視圖函數。每個視圖函數都使用HttpRequest對象作為第一個參數,並且通常稱之為request

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

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

在視圖層最重要的就是要熟悉兩個對象:請求對象(request)和響應對象(HttpResponse)。

HttpRequest 對象

request 屬性

Django 將請求報文中的請求行、請求頭、請求體封裝成 HttpRequest 類中的屬性。除了特殊說明之外,其他的均為只讀屬性。

1.HttpRequest.GET

  一個類似於字典的對象,包含 HTTP GET 的所有參數。詳情請參考 QueryDict 對象。

2.HttpRequest.POST

  一個類似於字典的對象,如果請求中包含表單數據,則將這些數據封裝成 QueryDict 對象。

  POST 請求可以帶有空的 POST 字典 —— 如果通過 HTTP POST 方法發送一個表單,但是表單中沒有任何的數據,QueryDict 對象依然會被創建。
   因此,不應該使用 if request.POST  來檢查使用的是否是POST 方法;應該使用 if request.method == "POST"
  另外:如果使用 POST 上傳文件的話,文件信息將包含在 FILES 屬性中。
   
   註意:鍵值對的值是多個的時候,比如checkbox類型的input標簽,select標簽,需要用:
        request.POST.getlist("hobby")

3.HttpRequest.body

  一個字符串,代表請求報文的主體。在處理非 HTTP 形式的報文時非常有用,例如:二進制圖片、XML,Json等。
  但是,如果要處理表單數據的時候,推薦還是使用 HttpRequest.POST 。


4.HttpRequest.path

  一個字符串,表示請求的路徑組件(不含域名)。
  例如:"/music/bands/the_beatles/"

5.HttpRequest.method

  一個字符串,表示請求使用的HTTP 方法。必須使用大寫。
  例如:"GET"、"POST"


6.HttpRequest.encoding

  一個字符串,表示提交的數據的編碼方式(如果為 None 則表示使用 DEFAULT_CHARSET 的設置,默認為 ‘utf-8‘)。
   這個屬性是可寫的,你可以修改它來修改訪問表單數據使用的編碼。
   接下來對屬性的任何訪問(例如從 GET 或 POST 中讀取數據)將使用新的 encoding 值。
   如果你知道表單數據的編碼不是 DEFAULT_CHARSET ,則使用它。


7.HttpRequest.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 鍵。

8.HttpRequest.FILES

  一個類似於字典的對象,包含所有的上傳文件信息。
   FILES 中的每個鍵為<input type="file" name="" /> 中的name,值則為對應的數據。
  註意,FILES 只有在請求的方法為POST 且提交的<form> 帶有enctype="multipart/form-data" 的情況下才會
   包含數據。否則,FILES 將為一個空的類似於字典的對象。


9.HttpRequest.COOKIES

  一個標準的Python 字典,包含所有的cookie。鍵和值都為字符串。



10.HttpRequest.session

   一個既可讀又可寫的類似於字典的對象,表示當前的會話。只有當Django 啟用會話的支持時才可用。
    完整的細節參見會話的文檔。


11.HttpRequest.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 中間件時才可用。

     -------------------------------------------------------------------------------------

    匿名用戶
    class 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。

request常用方法

1.HttpRequest.get_full_path()

  返回 path,如果可以將加上查詢字符串。

  例如:"/music/bands/the_beatles/?print=true"
  註意和path的區別:http://127.0.0.1:8001/order/?name=lqz&age=10

2.HttpRequest.is_ajax()

  如果請求是通過XMLHttpRequest 發起的,則返回True,方法是檢查 HTTP_X_REQUESTED_WITH 相應的首部是否是字符串‘XMLHttpRequest‘。

  大部分現代的 JavaScript 庫都會發送這個頭部。如果你編寫自己的 XMLHttpRequest 調用(在瀏覽器端),你必須手工設置這個值來讓 is_ajax() 可以工作。

  如果一個響應需要根據請求是否是通過AJAX 發起的,並且你正在使用某種形式的緩存例如Django 的 cache middleware,
   你應該使用 vary_on_headers(‘HTTP_X_REQUESTED_WITH‘) 裝飾你的視圖以讓響應能夠正確地緩存。

HttpResponse 對象

響應對象主要有三種形式:前面的博客有相關介紹

  1. HttpResponse();
  2. render();
  3. redirect().

HttpResponse()括號內直接跟一個具體的字符串作為響應體,比較直接和簡單,所以這裏介紹後兩種

render()

render(request, template_name[, context])

結合給定的模板和一個上下文字典,返回一個經過 Django渲染後的HttpResponse 對象到前端。

參數:

  1. request:用於生成響應的請求對象;
  2. template_name:要使用的模板的完整名稱,可選的參數;
  3. context:添加到模板上下文的一個字典。默認是一個空字典,如果字典中的某個值是可調用的,視圖將在渲染模板之前調用它;

render 方法就是將一個模板頁面中的模板語法進行渲染,最終渲染成一個 HTML 頁面作為響應返回給前端。

redirect()

傳遞要重定向的一個硬編碼的URL

def my_view(request):
    ...
    return redirect(‘/some/url/‘)

也可以是一個完成的 url

def my_view(request):
    ...
    return redirect(‘http://www.baidu.com/‘) 

JsonResponse對象

向前端返回一個 json 格式的字符串,有兩種方式:

第一種方式

import json
data = {‘name‘: ‘musibii‘, ‘age‘: 18}
return HttpResponse(json.dumps(data))
# 從這裏可以看出其實 Jsonresponse內部也是調用了 json 的 dumps 方法進行轉換

第二種方式

from django.http import JsonResponse
data = {‘name‘: ‘musibii‘, ‘age‘: 18}

return JsonResponse(data1,safe=False)
# safe參數是一種安全機制,因為如果要傳輸列表類型的數據時,會因為內部的相關機制會產生錯誤。

CBV 和 FBV

CBV 基於類的視圖(Class Base View)和 FBV 基於函數的視圖(Function Base View)

CBV

from django.views import View

class Login(View):
    
    def dispatch(self, request, *args, **kwargs):
        print(request)
        print(args)
        print(kwargs)
        # 可以寫類似裝飾器的東西,在前後加代碼
        obj=super().dispatch(request, *args, **kwargs)
        return obj

    def get(self, request):
        return render(request, ‘login.html‘)

    def post(self, request):
        name = request.POST.get(‘name‘)
        ret = BookInfo.objects.filter(name=name).first()
        print(type(ret))
        dic = {}
        print(ret.name)
        print(type(ret.__dict__))
        for key, value in (ret.__dict__).items():
            if key[0].startswith(‘_‘):
                continue
            print(key)
            dic[key] = value
            # print(dic)

        # if ret:
        #     dic = {}
        #     for key in ret.items():
        #         dic[key] = ret
        return JsonResponse(dic, json_dumps_params={‘ensure_ascii‘: False})

FBV

def file_upload(request):
    if request.method=="GET":
        return render(request,‘file_upload.html‘)
    else:
        print(request.POST)
        print(request.FILES)
        # print(request.body)
        # FILES是一個字典,
        # <MultiValueDict: {‘myfile‘: [<InMemoryUploadedFile: 1.jpg (image/jpeg)>]}>
        # 拿到上傳的文件對象

        file=request.FILES.get(‘myfile‘)
        print(type(file))
        from django.core.files.uploadedfile import InMemoryUploadedFile
        with open(file.name,‘wb‘) as f:
            # for line in file.chunks():
            for line in file:
                f.write(line)
        return HttpResponse(‘上傳成功‘)

簡單文件上傳

模板文件

# upload_file.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<form action="" method="post" enctype="multipart/form-data">
    <p>用戶名:<input type="text" name="name"></p>
    <input type="file" name="myfile">
{#    <input type="file" name="myfile2">#}
    <input type="submit" value="提交">

</form>

</body>
</html>

路由文件

from django.conf.urls import url
from app01 import views
urlpatterns = [
    url(r‘^upload_file/$‘, views.UploadFile.as_view()),
]

視圖文件

class UploadFile(View):

    def get(self, request):
        return render(request, ‘upload_file.html‘)

    def post(self, request):
        file = request.FILES.get(‘myfile‘)
        # print(file[‘file‘])
        from django.core.files.uploadedfile import InMemoryUploadedFile
        print(time.time())
        filename = str(time.time()).split(‘.‘)[0] + file.name
        with open(filename, ‘wb‘) as f:
            for line in file:
                f.write(line)
        return HttpResponse(‘上傳成功‘)

Django 系列博客(七)