Django的View(視圖)


 

 

 原文鏈接:https://www.cnblogs.com/maple-shaw/articles/9285269.html

一個視圖函數(類),簡稱視圖,是一個簡單的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對象。


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頁面。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM