Django的View(視圖)

 

 

 原文連接:https://www.cnblogs.com/maple-shaw/articles/9285269.htmlhtml

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

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

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


一個簡單的視圖

下面是一個以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)
複製代碼

讓咱們來逐行解釋下上面的代碼:web

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

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

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

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

Django使用請求和響應對象來經過系統傳遞狀態。json

當瀏覽器向服務端請求一個頁面時,Django建立一個HttpRequest對象,該對象包含關於請求的元數據。而後,Django加載相應的視圖,將這個HttpRequest對象做爲第一個參數傳遞給視圖函數。

每一個視圖負責返回一個HttpResponse對象。


CBV和FBV

CBV  :class based views       FBV :function based views

咱們以前寫過的都是基於函數的view,就叫FBV。還能夠把view寫成基於類的。

就拿咱們以前寫過的添加班級爲例:

FBV版:

複製代碼
# FBV版添加班級
def add_class(request):
    if request.method == "POST":
        class_name = request.POST.get("class_name")
        models.Classes.objects.create(name=class_name)
        return redirect("/class_list/")
    return render(request, "add_class.html")
複製代碼

CBV版:

複製代碼
# CBV版添加班級
from django.views import View


class AddClass(View):

    def get(self, request):
        return render(request, "add_class.html")

    def post(self, request):
        class_name = request.POST.get("class_name")
        models.Classes.objects.create(name=class_name)
        return redirect("/class_list/")
複製代碼

注意:

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

# urls.py中
url(r'^add_class/$', views.AddClass.as_view()),

將函數改成類(FBV->CBV)案例,即定義CBV的寫法:

新增出版社函數分get請求和post請求,各自返回不一樣的內容,邏輯代碼以下

# 新增出版社
def add_publisher(request):
    error = ''
    # 對請求方式進行判斷
    if request.method == 'POST':
        # 處理POST請求
        # 獲取到出版社的名稱
        publisher_name = request.POST.get('publisher_name')
        # 判斷出版社名稱是否有重複的
        if models.Publisher.objects.filter(name=publisher_name):
            error = '出版社名稱已存在'
        # 判斷輸入的值是否爲空
        if not publisher_name:
            error = '不能輸入爲空'
        if not error:
            # 使用ORM將數據插入到數據庫中
            obj = models.Publisher.objects.create(name=publisher_name)
            # 跳轉到展現出版社的頁面
            return redirect('/publisher_list/')
    # 返回一個包含form表單的頁面
    return render(request, 'add_publisher.html', {'error': error})
新增出版社函數

1)咱們要將函數改爲類,這個類不能直接只是咱們本身寫,由於須要不少功能,本身寫的而後須要繼承Django的View類。
2)定義的類中寫get和post等方法,須要接收參數request。方法中是各自請求作的操做以及返回的一個響應
3)當請求到來時根據請求方法會執行對應的方法,而後返回響應的內容

4)這樣執行的好處是把各個請求的代碼分開了,是邏輯更清楚

5)若是還有其餘的請求,那麼繼續寫對應請求的方法。

6)使用CBV:將以前對應的函數寫成對應的類,而且模塊.類.as_views() 執行,這樣就實現了CBV寫法,get請求就走get方法

若是沒有加as_views(),那麼報錯:

沒有執行報錯:

如此纔是正確的

url(r'^add_publisher/', views.AddPblisher.as_view()),
 

as_view的流程

爲何瀏覽器get請求,views裏面就找到get請求;爲何瀏覽器post請求,類中就執行post的方法?
當執行到as_view()的時候點擊進去看一下這個方法
class View(object):
    """
    Intentionally simple parent class for all views. Only implements
    dispatch-by-method and simple sanity checking.
    """

    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

    def __init__(self, **kwargs):
        """
        Constructor. Called in the URLconf; can contain helpful extra
        keyword arguments, and other things.
        """
        # Go through keyword arguments, and either save their values to our
        # instance, or raise an error.
        for key, value in six.iteritems(kwargs):
            setattr(self, key, value)

    @classonlymethod
    def as_view(cls, **initkwargs):
        """
        Main entry point for a request-response process.
        """
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.request = request
            self.args = args
            self.kwargs = kwargs
            return self.dispatch(request, *args, **kwargs)
        view.view_class = cls
        view.view_initkwargs = initkwargs

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view
as_view方法源碼

cls指的是當前的類,就是咱們在views模塊中定義的類,url中執行了as_view(),由下圖可知它的返回值是view,也就是views.AddPblisher.as_view()執行就是定義了一個view方法並返回的就是這個view方法。

這就是至關於從上到下執行,而後到了這裏就執行view函數

下面再看view函數作的

 

cls()將本身寫的類AddPblisher實例化一個對象,並賦予self。而後這個子函數view中就能以self代替AddPblisher的對象作操做。
view函數中將request賦值給實例變量self.request,這樣就能在CBV方式的類中get或其它方法裏使用self.request,從而操做請求對象。而在FBV方式中直接使用request。

    

class View(object):
    """
    Intentionally simple parent class for all views. Only implements
    dispatch-by-method and simple sanity checking.
    """

    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

    def __init__(self, **kwargs):
        """
        Constructor. Called in the URLconf; can contain helpful extra
        keyword arguments, and other things.
        """
        # Go through keyword arguments, and either save their values to our
        # instance, or raise an error.
        for key, value in six.iteritems(kwargs):
            setattr(self, key, value)

    @classonlymethod
    def as_view(cls, **initkwargs):  #cls指的是當前的類,就是咱們在views模塊中定義的類
        """
        Main entry point for a request-response process.
        """
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))

        def view(request, *args, **kwargs): 
            self = cls(**initkwargs) # cls()將本身寫的類AddPblisher實例化一個對象,並賦予self。而後在類AddPblisher中就能以self代替AddPblisher的對象作操做。
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.request = request  #在這裏request和self.request是同樣的,這樣作是爲了在本身建立的類中能用self.的方法
            self.args = args
            self.kwargs = kwargs
            return self.dispatch(request, *args, **kwargs)  #view返回的是dispatch方法的返回值。
        view.view_class = cls 
        view.view_initkwargs = initkwargs

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view
        
    def dispatch(self, request, *args, **kwargs):
        # Try to dispatch to the right method; if a method doesn't exist,
        # defer to the error handler. Also defer to the error handler if the
        # request method isn't on the approved list.  #http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
        if request.method.lower() in self.http_method_names:  #若是請求方式變小寫後在上面的列表中,那麼讓handler經過反射獲取到你的請求方法,不然讓它
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)   #等於 self.http_method_not_allowed
        else:
            handler = self.http_method_not_allowed  #不然handler等於 self.http_method_not_allowed 方法
        return handler(request, *args, **kwargs)  #返回handler的執行結果給as_view中的view,有人調用as_view方法是,那麼返回的就是view的返回值,view方法的返回值是
                #當url中執行了as_view方法時,返回的是view這個實現閉包並封裝了數據的函數地址,url中至關於寫的是view這個函數,那麼url路由以後會執行view這個函數。
                view執行的返回值就是dispatch執行的返回值。dispatch執行的返回值就是handler函數的返回值。[1]若是瀏覽器的請求方式在個人方式列表中(通常是8個)存在,那麼handler就是self對象自己和請求方式經過反射獲取到小寫的請求方式的方法。[2]若是不是那麼讓它等於另外一個方法。[3]若是handler是【1】,那麼get方法首先從我寫的類中找,咱們在類中寫了get請求方法,那麼handler執行的返回值就是咱們get的返回內容。也就是咱們在本身建立的類中寫了對應請求的方法,那麼就會執行咱們寫的對應請求的方法。[4]若是handler是【2】,那麼返回的self.http_method_not_allowed是不被容許訪問的方法執行返回結果。即打印405錯誤碼並返回執行不被容許的的返回方法。
    def http_method_not_allowed(self, request, *args, **kwargs):
        logger.warning(
            'Method Not Allowed (%s): %s', request.method, request.path,
            extra={'status_code': 405, 'request': request}
        )
        return http.HttpResponseNotAllowed(self._allowed_methods())
View Code

#當url中執行了as_view方法時,返回的是view這個實現閉包並封裝了數據的函數地址,url中至關於寫的是view這個函數,那麼url路由以後會執行view這個函數。
view執行的返回值就是dispatch執行的返回值。dispatch執行的返回值就是handler函數的返回值。[1]若是瀏覽器的請求方式在個人方式列表中(通常是8個)存在,那麼handler就是self對象自己和請求方式經過反射獲取到小寫的請求方式的方法。[2]若是不是那麼讓它等於另外一個方法。[3]若是handler是【1】,那麼get方法首先從我寫的類中找,咱們在類中寫了get請求方法,那麼handler執行的返回值就是咱們get的返回內容。也就是咱們在本身建立的類中寫了對應請求的方法,那麼就會執行咱們寫的對應請求的方法。[4]若是handler是【2】,那麼返回的self.http_method_not_allowed是不被容許訪問的方法執行返回結果。即打印405錯誤碼並返回執行不被容許的的返回方法。

若是get不到也是執行不被容許的方法

base.py
class View(object):
    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
     @classonlymethod
    def as_view(cls, **initkwargs):
            def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.request = request
            self.args = args
            self.kwargs = kwargs
            return self.dispatch(request, *args, **kwargs)
        view.view_class = cls
        .................
        return view
    def dispatch(self, request, *args, **kwargs):
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)
     def http_method_not_allowed(self, request, *args, **kwargs):
        logger.warning(
            'Method Not Allowed (%s): %s', request.method, request.path,
            extra={'status_code': 405, 'request': request}
        )
        return http.HttpResponseNotAllowed(self._allowed_methods())
        
        
另外一個文件:response.py
class HttpResponseNotAllowed(HttpResponse):
    status_code = 405

    def __init__(self, permitted_methods, *args, **kwargs):
        super(HttpResponseNotAllowed, self).__init__(*args, **kwargs)
        self['Allow'] = ', '.join(permitted_methods)

    def __repr__(self):
        return '<%(cls)s [%(methods)s] status_code=%(status_code)d%(content_type)s>' % {
            'cls': self.__class__.__name__,
            'status_code': self.status_code,
            'content_type': self._content_type_for_repr,
            'methods': self['Allow'],
        }
源碼提取
我將源碼中的這個類變量在我寫的子類中重寫。那麼源碼首先在我這個列表中找,由於post等請求在子類列表中找不到,那麼它執行不被容許的方法,返回的是405錯誤碼

以下,我要在子中重寫源碼中的dispatch()方法。由於dispatch執行的結果就是返回的頁面的內容,2處找這個方法先在1處尋找,找到後給瀏覽器返回ok,那麼此時不管什麼請求都是返回的ok,不符合咱們的須要

咱們重寫dispatch這個方法,那麼就至關因而須要給這個方法添加功能。函數中裝飾器能夠給另外的函數添加功能,那麼重寫方法時咱們能夠super執行父類中的方法並接收返回值,在執行父類中方法的先後作些其它的操做給它添加功能。

 

若是將super先後換成時間,那麼就能計算dispatch執行時間

 

給視圖加裝飾器

使用裝飾器裝飾FBV

FBV自己就是一個函數,因此和給普通的函數加裝飾器無差:

複製代碼
def wrapper(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        ret = func(*args, **kwargs)
        end_time = time.time()
        print("used:", end_time-start_time)
        return ret
    return inner


# FBV版添加班級
@wrapper
def add_class(request):
    if request.method == "POST":
        class_name = request.POST.get("class_name")
        models.Classes.objects.create(name=class_name)
        return redirect("/class_list/")
    return render(request, "add_class.html")
複製代碼

使用裝飾器裝飾CBV

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

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

複製代碼
# CBV版添加班級
from django.views import View
from django.utils.decorators import method_decorator

class AddClass(View):

    @method_decorator(wrapper)
    def get(self, request):
        return render(request, "add_class.html")

    def post(self, request):
        class_name = request.POST.get("class_name")
        models.Classes.objects.create(name=class_name)
        return redirect("/class_list/")
複製代碼
# 使用CBV時要注意,請求過來後會先執行dispatch()這個方法,若是須要批量對具體的請求處理方法,如get,post等作一些操做的時候,這裏咱們能夠手動改寫dispatch方法,這個dispatch方法就和在FBV上加裝飾器的效果同樣。

class Login(View):
     
    def dispatch(self, request, *args, **kwargs):
        print('before')
        obj = super(Login,self).dispatch(request, *args, **kwargs)
        print('after')
        return obj
 
    def get(self,request):
        return render(request,'login.html')
 
    def post(self,request):
        print(request.POST.get('user'))
        return HttpResponse('Login.post')
關於CBV擴展閱讀

咱們在視圖函數中給類中方法加裝飾器,先導入某個類。而後直接在get等方法前面@方式裝飾器(裝飾器函數名)。若是想要全部請求加上裝飾器,那麼重寫dispatch方法並執行父的方法,而後給dispatch方法添加這個裝飾器。 

還能夠加多個,指定方法名字:

在類中方法添加裝飾器,能夠直接

@裝飾器名

def  方法名字(self):

 

使用和不使用method_decorator的區別以下,主要是不使用時參數第一個是本身寫的類對象,傳到裝飾器的第一個參數是方法中的self,第二個纔是 個人request對象;使用時那麼裝飾器中第一個參數就是request

所以裝飾器中要使用request對象,那麼須要判斷第幾個形參是它:

 

Request對象和Response對象


當get訪問一個網頁的時候,GET和POST返回的都是這裏類型的,

當get請求後面有拼接的內容的時候,

get就有值了

當訪問是這個路徑的時候,打印一下request.path_info   返回用戶訪問路徑,不包括ip和端口

 打印body,由於get沒有請全體,是沒有內容的bytes類型。請求行,請求頭,請求體

post請求中打印請求體:print('request.body'),數據會進行url編碼,原始的數據編碼了的

 

 

request對象

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

官方文檔

 

請求相關的經常使用值

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

屬性

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

0.HttpRequest.scheme
表示請求方案的字符串(一般爲http或https),即瀏覽器請求協議http https

print(request,type(request))   # <WSGIRequest: GET '/test/'> <class 'django.core.handlers.wsgi.WSGIRequest'>

 

屬性:
  django將請求報文中的請求行、頭部信息、內容主體封裝成 HttpRequest 類中的屬性。
   除了特殊說明的以外,其餘均爲只讀的。


0.HttpRequest.scheme
   表示請求方案的字符串(一般爲http或https)

1.HttpRequest.body

  一個字符串,表明請求報文的主體。在處理非 HTTP 形式的報文時很是有用,例如:二進制圖片、XML,Json等。

  可是,若是要處理表單數據的時候,推薦仍是使用 HttpRequest.POST 。

  另外,咱們還能夠用 python 的類文件方法去操做它,詳情參考 HttpRequest.read() 。

 

2.HttpRequest.path

  一個字符串,表示請求的路徑組件(不含域名)。

  例如:"/music/bands/the_beatles/"



3.HttpRequest.method

  一個字符串,表示請求使用的HTTP 方法。必須使用大寫。

  例如:"GET""POST"

 

4.HttpRequest.encoding

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

 

5.HttpRequest.GET 

  一個相似於字典的對象,包含 HTTP GET 的全部參數。詳情請參考 QueryDict 對象。

 

6.HttpRequest.POST

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

  POST 請求能夠帶有空的 POST 字典 —— 若是經過 HTTP POST 方法發送一個表單,可是表單中沒有任何的數據,QueryDict 對象依然會被建立。
   所以,不該該使用 if request.POST  來檢查使用的是不是POST 方法;應該使用 if request.method == "POST" 

  另外:若是使用 POST 上傳文件的話,文件信息將包含在 FILES 屬性中。

 7.HttpRequest.COOKIES

  一個標準的Python 字典,包含全部的cookie。鍵和值都爲字符串。

 

8.HttpRequest.FILES

  一個相似於字典的對象,包含全部的上傳文件信息。
   FILES 中的每一個鍵爲<input type="file" name="" /> 中的name,值則爲對應的數據。

  注意,FILES 只有在請求的方法爲POST 且提交的<form> 帶有enctype="multipart/form-data" 的狀況下才會
   包含數據。不然,FILES 將爲一個空的相似於字典的對象。

 

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

 
10.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。

 

11.HttpRequest.session

   一個既可讀又可寫的相似於字典的對象,表示當前的會話。只有當Django 啓用會話的支持時纔可用。
    完整的細節參見會話的文檔。
request屬性相關
{
    'ALLUSERSPROFILE': 'C:\\ProgramData',
    'APPDATA': 'C:\\Users\\Administrator\\AppData\\Roaming',
    'COMMONPROGRAMFILES': 'C:\\Program Files (x86)\\Common Files',
    'COMMONPROGRAMFILES(X86)': 'C:\\Program Files (x86)\\Common Files',
    'COMMONPROGRAMW6432': 'C:\\Program Files\\Common Files',
    'COMPUTERNAME': 'PC-20190328RVNA',
    'COMSPEC': 'C:\\Windows\\system32\\cmd.exe',
    'DJANGO_SETTINGS_MODULE': 'bookmanager.settings',
    'FP_NO_HOST_CHECK': 'NO',
    'HOMEDRIVE': 'C:',
    'HOMEPATH': '\\Users\\Administrator',
    'LOCALAPPDATA': 'C:\\Users\\Administrator\\AppData\\Local',
    'LOGONSERVER': '\\\\PC-20190328RVNA',
    'NUMBER_OF_PROCESSORS': '4',
    'OS': 'Windows_NT',
    'PATH': 'C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\軟件安裝\\Git\\cmd;C:\\mysql\\mysql-5.6.44-winx64\\bin;C:\\python3\\Scripts\\;C:\\python3\\',
    'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC',
    'PROCESSOR_ARCHITECTURE': 'x86',
    'PROCESSOR_ARCHITEW6432': 'AMD64',
    'PROCESSOR_IDENTIFIER': 'Intel64 Family 6 Model 58 Stepping 9, GenuineIntel',
    'PROCESSOR_LEVEL': '6',
    'PROCESSOR_REVISION': '3a09',
    'PROGRAMDATA': 'C:\\ProgramData',
    'PROGRAMFILES': 'C:\\Program Files (x86)',
    'PROGRAMFILES(X86)': 'C:\\Program Files (x86)',
    'PROGRAMW6432': 'C:\\Program Files',
    'PSMODULEPATH': 'C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules\\',
    'PUBLIC': 'C:\\Users\\Public',
    'PYCHARM_HOSTED': '1',
    'PYCHARM_MATPLOTLIB_PORT': '50724',
    'PYTHONIOENCODING': 'UTF-8',
    'PYTHONPATH': 'C:\\mcw\\bookmanager;C:\\軟件安裝\\PyCharm 2018.3.5\\helpers\\pycharm_matplotlib_backend',
    'PYTHONUNBUFFERED': '1',
    'SESSIONNAME': 'Console',
    'SYSTEMDRIVE': 'C:',
    'SYSTEMROOT': 'C:\\Windows',
    'TEMP': 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp',
    'TMP': 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp',
    'USERDOMAIN': 'PC-20190328RVNA',
    'USERNAME': 'Administrator',
    'USERPROFILE': 'C:\\Users\\Administrator',
    'WINDIR': 'C:\\Windows',
    'WINDOWS_TRACING_FLAGS': '3',
    'WINDOWS_TRACING_LOGFILE': 'C:\\BVTBin\\Tests\\installpackage\\csilogfile.log',
    'RUN_MAIN': 'true',
    'SERVER_NAME': 'PC-20190328RVNA',
    'GATEWAY_INTERFACE': 'CGI/1.1',
    'SERVER_PORT': '8000',
    'REMOTE_HOST': '',
    'CONTENT_LENGTH': '',
    'SCRIPT_NAME': '',
    'SERVER_PROTOCOL': 'HTTP/1.1',
    'SERVER_SOFTWARE': 'WSGIServer/0.2',
    'REQUEST_METHOD': 'GET',
    'PATH_INFO': '/test/',
    'QUERY_STRING': '',
    'REMOTE_ADDR': '127.0.0.1',
    'CONTENT_TYPE': 'text/plain',
    'HTTP_HOST': '127.0.0.1:8000',
    'HTTP_CONNECTION': 'keep-alive',
    'HTTP_CACHE_CONTROL': 'max-age=0',
    'HTTP_UPGRADE_INSECURE_REQUESTS': '1',
    'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36',
    'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
    'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br',
    'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9',
    'HTTP_COOKIE': 'csrftoken=e17Wb68amZdumsRa67LXuTzmdEjaA2lYLj0OkmjiiR2SKPRXBHoYcB3c929v8csg',
    'wsgi.input': < _io.BufferedReader name = 776 > ,
    'wsgi.errors': < _io.TextIOWrapper name = '<stderr>'
    mode = 'w'
    encoding = 'UTF-8' > ,
    'wsgi.version': (1, 0),
    'wsgi.run_once': False,
    'wsgi.url_scheme': 'http',
    'wsgi.multithread': True,
    'wsgi.multiprocess': False,
    'wsgi.file_wrapper': < class 'wsgiref.util.FileWrapper' > ,
    'CSRF_COOKIE': 'e17Wb68amZdumsRa67LXuTzmdEjaA2lYLj0OkmjiiR2SKPRXBHoYcB3c929v8csg'
}
HttpRequest.META

上傳文件示例

def upload(request):
    """
    保存上傳文件前,數據須要存放在某個位置。默認當上傳文件小於2.5M時,django會將上傳文件的所有內容讀進內存。從內存讀取一次,寫磁盤一次。
    但當上傳文件很大時,django會把上傳文件寫到臨時文件中,而後存放到系統臨時文件夾中。
    :param request: 
    :return: 
    """
    if request.method == "POST":
        # 從請求的FILES中獲取上傳文件的文件名,file爲頁面上type=files類型input的name屬性值
        filename = request.FILES["file"].name
        # 在項目目錄下新建一個文件
        with open(filename, "wb") as f:
            # 從上傳的文件對象中一點一點讀
            for chunk in request.FILES["file"].chunks():
                # 寫入本地文件
                f.write(chunk)
        return HttpResponse("上傳OK")
上傳文件示例代碼

1)post表單中寫一個類型是file的input標籤,起個name,給個button。

瀏覽器上傳一張圖片,查看文件是否獲取到,結果爲空

 須要修改傳輸數據類型:

默認用的是下面這個:post請求中打印請求體:print('request.body'),數據會進行url編碼,指的就是這個編碼

而後上傳文件:文件在request的FILES屬性中

 一個相似於字典的對象,包含全部的上傳文件信息。
   FILES 中的每一個鍵爲<input type="file" name="" /> 中的name,值則爲對應的數據。

  注意,FILES 只有在請求的方法爲POST 且提交的<form> 帶有enctype="multipart/form-data" 的狀況下才會
   包含數據。不然,FILES 將爲一個空的相似於字典的對象。
get獲取到這個文件對象。默認位置在項目根目錄,獲取到f1的值,值.name是文件名

全路徑:若是有拼接的內容也會獲取到。不包含IP端口,可是包含參數

 

 

 

方法

1.HttpRequest.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() 方法將會失敗。除非使用中間件重寫代理的首部。

 

2.HttpRequest.get_full_path()

  返回 path,若是能夠將加上查詢字符串。

  例如:"/music/bands/the_beatles/?print=true"

 

3.HttpRequest.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
        複製代碼
         


4.HttpRequest.is_secure()

  若是請求時是安全的,則返回True;即請求通是過 HTTPS 發起的。

 

5.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') 裝飾你的視圖以讓響應可以正確地緩存。
請求相關方法

 

注意:鍵值對的值是多個的時候,好比checkbox類型的input標籤,select標籤,須要用:

request.POST.getlist("hobby")

Response對象

與由Django自動建立的HttpRequest對象相比,HttpResponse對象是咱們的職責範圍了。咱們寫的每一個視圖都須要實例化,填充和返回一個HttpResponse。

若是沒有返回報錯,須要返回一個HttpResponse對象

HttpResponse類位於django.http模塊中。

 

render參數,有request 模板 上下文就是咱們傳的變量(字典)
下面render_to_string方法會拿到模板和變量作字符串替換。返回的content就是渲染以後的html字符串。這個content內容又給了HttpResponse對象。也就是說render裏面返回的仍是HttpResponse對象,只是裏面調用其它的方法把這個模板渲染好了。這裏是Django的模板引擎,也能夠換成jinja2的。

 

再看看redirect,拿到的是兩個類:,返回的而是其中一個類的對象

下面這個是永久跳轉的類,

永久跳轉的類繼承的重定向基本的類,又是繼承的HttpResponse這個類:location就是咱們在開發這個工具中看到的重定向中的那個屬性。因此重定向也是繼承的HttpResponse。

 

它也是繼承的這個對象:

 

 

使用

傳遞字符串

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

response = JsonResponse({'foo': 'bar'})
print(response.content)

b'{"foo": "bar"}'
複製代碼

默認只能傳遞字典類型,若是要傳遞非字典類型須要設置一下safe關鍵字參數。

response = JsonResponse([1, 2, 3], safe=False)

定義一個字典返回給瀏覽器

顯示的是name和age連到一塊的:將字典迭代拿的裏面的key

可是我想要的是完整的字典,那麼我就要本身作json序列化了:

顯示的是完整的字典內容

 

那麼還可使用另外一個類作返回內容傳遞字典數據,直接將字典放到JsonResponse對象中

效果以下:跟本身作的json序列化比起來,小了點細了點。而且告訴瀏覽器當前發送數據內容的類型。若是是在使用ajax,那麼它遇到這個內容它本身會作反序列化。前端發送一個請求,回來以後它看到這個類型本身會將這個數據反序列化,變成前端使用的對象,而後點的方法取裏面的內容。

若是是本身作序列化的話那麼響應頭的內容類型以下。發送的是普通文本,它不會給你作任 何變化,你想用的話就須要本身作反序列化

咱們也能夠本身序列化發送,可是須要修改這個內容類型的屬性,這樣也能夠達到同樣的效果。用字典從新賦值的方式修改它的屬性值

 

HttpResponse默認返回的是text/html    charset utf-8     JsonResponse默認返回的是 application/json 。

下面能夠找一下它的默認的:

 

 

這個settings點不進去,它是全局配置文件裏的:由下能夠看到HttpResponse返回的是默認類型

由下能夠看出沒有傳默認參數那麼使用的是text/html,

那麼把內容類型傳遞一個參數修改爲別的也能夠改變它的類型:

由下能夠看到JsonResponse默認的就是 application/json 。JsonReponse會給你作序列化,而且響應頭給你設置成application/json,有了這個以後,前端拿到數據就自動反序列化

 那麼其它類型的好比列表傳輸呢?報錯: In order to allow non-dict objects to be serialized set the safe parameter to False.

那麼根據提示設置safe爲False前端就拿到數據了。這是由於JsonResponse默認只支持序列化字典類型的。若是是非字典類型的那麼就要加上safe=False

Django shortcut functions

官方文檔

render()

結合一個給定的模板和一個給定的上下文字典,並返回一個渲染後的 HttpResponse 對象。

參數:

  • request: 用於生成響應的請求對象。
  • template_name:要使用的模板的完整名稱,可選的參數
  • context:添加到模板上下文的一個字典。默認是一個空字典。若是字典中的某個值是可調用的,視圖將在渲染模板以前調用它。
  • content_type:生成的文檔要使用的MIME類型。默認爲 DEFAULT_CONTENT_TYPE 設置的值。默認爲'text/html'
  • status:響應的狀態碼。默認爲200。
  • useing: 用於加載模板的模板引擎的名稱。

一個簡單的例子:

from django.shortcuts import render

def my_view(request):
    # 視圖的代碼寫在這裏
    return render(request, 'myapp/index.html', {'foo': 'bar'})

上面的代碼等於:

複製代碼
from django.http import HttpResponse
from django.template import loader

def my_view(request):
    # 視圖代碼寫在這裏
    t = loader.get_template('myapp/index.html')
    c = {'foo': 'bar'}
    return HttpResponse(t.render(c, request))
複製代碼

redirect()

參數能夠是:

  • 一個模型:將調用模型的get_absolute_url() 函數
  • 一個視圖,能夠帶有參數:將使用urlresolvers.reverse 來反向解析名稱
  • 一個絕對的或相對的URL,將原封不動的做爲重定向的位置。

默認返回一個臨時的重定向;傳遞permanent=True 能夠返回一個永久的重定向。

示例:

你能夠用多種方式使用redirect() 函數。

傳遞一個具體的ORM對象(瞭解便可)

將調用具體ORM對象的get_absolute_url() 方法來獲取重定向的URL:

複製代碼
from django.shortcuts import redirect
 
def my_view(request):
    ...
    object = MyModel.objects.get(...)
    return redirect(object)
複製代碼

傳遞一個視圖的名稱

def my_view(request):
    ...
    return redirect('some-view-name', foo='bar')

傳遞要重定向到的一個具體的網址

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

固然也能夠是一個完整的網址

def my_view(request):
    ...
    return redirect('http://example.com/')

默認狀況下,redirect() 返回一個臨時重定向。以上全部的形式都接收一個permanent 參數;若是設置爲True,將返回一個永久的重定向:

def my_view(request):
    ...
    object = MyModel.objects.get(...)
    return redirect(object, permanent=True)  

擴展閱讀: 

臨時重定向(響應狀態碼:302)和永久重定向(響應狀態碼:301)對普通用戶來講是沒什麼區別的,它主要面向的是搜索引擎的機器人。

A頁面臨時重定向到B頁面,那搜索引擎收錄的就是A頁面。

A頁面永久重定向到B頁面,那搜索引擎收錄的就是B頁面。

相關文章
相關標籤/搜索