第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

全部開發(fā)者教程

Django 入門教程

課程導(dǎo)學(xué)
Django 慕課教程使用指南
Django開發(fā)實(shí)戰(zhàn)
35 開發(fā)實(shí)戰(zhàn)

Django 視圖函數(shù)

Django 中的視圖是 MTV 架構(gòu)模式中的 V 層,主要處理客戶端的請(qǐng)求并生成響應(yīng)數(shù)據(jù)返回。它其實(shí)類似于 MVC 架構(gòu)模式中的 C 層,用于處理項(xiàng)目的業(yè)務(wù)邏輯部分。Django 的視圖函數(shù)就是 Django 項(xiàng)目中專門處理對(duì)應(yīng) path 的 python 函數(shù),這也是整個(gè)項(xiàng)目開發(fā)中最核心的業(yè)務(wù)處理部分。當(dāng)然,在 Django 中處理 path 路徑的不只有視圖函數(shù)(FBV),還有視圖類(CBV)。

1. Django 視圖中的 FBV 和 CBV

FBV 全稱是 function base views, 即在視圖中是使用函數(shù)來對(duì)應(yīng)處理請(qǐng)求。如下示例:

# 在django中會(huì)對(duì)跨域請(qǐng)求做攔截處理,這里使用@csrf_exempt注解表示不作處理,主要是針對(duì)POST請(qǐng)求
@csrf_exempt
def hello_world(request, *args, **kwargs):
    if request.method == 'GET':
        return HttpResponse('Hello, get request', content_type="text/plain")
    elif request.method == 'POST':
        return HttpResponse('Hello, post request', content_type="text/plain")
    return HttpResponse("Hello, world.", content_type="text/plain")

urlpatterns = [
    path('admin/', admin.site.urls),
    path('hello/', hello_world),
]

注意: 視圖函數(shù)可以接受 request 參數(shù),用于存放瀏覽器傳遞過來的所有數(shù)據(jù)。它是 WSGIRequest 類的一個(gè)實(shí)例,而 WSGIRequest 類又繼承自 HttpRequest 。我們通過 request 這個(gè)實(shí)例可以拿到客戶端 http 請(qǐng)求的方法,請(qǐng)求路徑、傳遞的參數(shù)以及上傳的文件等等。

CBV 全稱是 class base views 就是在視圖中使用類處理請(qǐng)求。在 Django 中加入了 CBV 的模式,讓我們用類去處理響應(yīng),這樣做有以下幾個(gè)好處:

  • 可以使用面向?qū)ο蟮募夹g(shù),比如 Mixin(多繼承),這樣可以有效復(fù)用增刪改查數(shù)據(jù)庫(kù)的代碼;

  • 在視圖類中我們定義如 get 方法就能處理對(duì)應(yīng)的 GET 請(qǐng)求,這樣無需使用 if 再進(jìn)行判斷,一方面提高了代碼的可讀性,另一方面也規(guī)范了 web api 的接口設(shè)計(jì)。

示例代碼:

from django.http import HttpResponse
from django.views import View

class HelloView(View):
    
    def get(self, request, *args, **kwargs):  
        return HttpResponse('get\n')
      
    def post(self, request, *args, **kwargs):  
        return HttpResponse('post\n')
    
    def put(self, request, *args, **kwargs):  
        return HttpResponse('put\n')
    
    def delete(self, request, *args, **kwargs):  
        return HttpResponse('delete\n')
    
    # 注意,給CBV加上@csrf_exempt注解,需要加到dispatch方法上,后續(xù)會(huì)詳解介紹這個(gè)函數(shù)的作用
    @csrf_exempt
    def dispatch(self, request, *args, **kwargs):
        return super(HelloView, self).dispatch(request, *args, **kwargs)
    
urlpatterns = [
    path('admin/', admin.site.urls),
    path('hello/', HelloView.as_view()),
]

將包含這個(gè)視圖層的 django demo 工程啟動(dòng)后,請(qǐng)求 /hello/ 路徑:

[root@server ~]# curl http://127.0.0.1:8881/hello/
get
[root@server ~]# curl -XPOST http://127.0.0.1:8881/hello/
post
[root@server ~]# curl -XPUT http://127.0.0.1:8881/hello/
put
[root@server ~]# curl -XDELETE http://127.0.0.1:8881/hello/
delete

可以看到,這樣封裝的視圖類對(duì)應(yīng)的 web api 接口具有良好的 restful 風(fēng)格,而且代碼可讀性也非常好。 后面我們會(huì)深入學(xué)習(xí)這種視圖模式以及 Django 內(nèi)部封裝的各種 view 類。

2. Django 中的 HttpRequest 類

上面我們初步接觸到了 HttpRequest 類,現(xiàn)在來詳細(xì)介紹下這個(gè)類及其相關(guān)屬性和方法。當(dāng) URLconf 文件匹配到客戶端的請(qǐng)求路徑后,會(huì)調(diào)用對(duì)應(yīng)的 FBV 或者 CBV,并將 HttpRequest 類的實(shí)例作為第一個(gè)參數(shù)傳入對(duì)應(yīng)的處理函數(shù)中。那么這個(gè) HttpRequest 類有哪些常用的屬性和方法呢?

常用屬性

  • HttpRequest.scheme:請(qǐng)求的協(xié)議,一般為 http 或者 https;

  • HttpRequest.body:請(qǐng)求主體;

  • HttpRequest.path: 所請(qǐng)求 URL 的完整路徑,即去掉協(xié)議,主機(jī)地址和端口后的路徑;

  • HttpRequest.method:客戶端 HTTP 請(qǐng)求方法,如 GET、POST、PUT、DELETE等;

  • HttpRequest.GET: 返回一個(gè) querydict 對(duì)象,該對(duì)象包含了所有的 HTTP 請(qǐng)求中 GET 請(qǐng)求的參數(shù);

  • HttpRequest.POST: 返回一個(gè) querydict 對(duì)象,該對(duì)象包含了所有的 HTTP 請(qǐng)求中 POST 請(qǐng)求的參數(shù);

  • HttpRequest.COOKIES:返回一個(gè)包含了所有 cookies 的字典;

  • HttpRequest.FILES:返回一個(gè)包含所有文件對(duì)象的字典。

常用方法

  • HttpRequest.get_host():返回客戶端發(fā)起請(qǐng)求的 IP + 端口;

  • HttpRequest.get_port():返回客戶端請(qǐng)求端口;

  • HttpRequest.get_full_path():返回請(qǐng)求的完整路徑,包括 “?” 后面所帶參數(shù);

  • HttpRequest.get_raw_uri():返回完整的 uri 地址,包括了協(xié)議、主機(jī)和端口以及完整請(qǐng)求路徑;

  • HttpRequest.build_absolute_uri():通過 request 實(shí)例中的地址和變量生成絕對(duì)的 uri 地址。

示例代碼

# 省略了import內(nèi)容

def hello_world(request, *args, **kwargs):
    request_info = ""
    request_info += "request.scheme={}\n".format(request.scheme)
    request_info += "request.body={}\n".format(request.body)
    request_info += "request.path={}\n".format(request.path)
    request_info += "request.method={}\n".format(request.method)
    request_info += "request.GET={}\n".format(request.GET)
    request_info += "request.FILES={}\n".format(request.FILES)

    request_info += "request.get_host={}\n".format(request.get_host())
    request_info += "request.get_port={}\n".format(request.get_port())
    request_info += "request.get_full_path={}\n".format(request.get_full_path())
    request_info += "request.get_raw_uri={}\n".format(request.get_raw_uri())
    request_info += "request.build_absolute_uri={}\n".format(request.build_absolute_uri())

    return HttpResponse(request_info, content_type="text/plain")

urlpatterns = [
    path('admin/', admin.site.urls),
    path('hello/', hello_world),
]

我們啟動(dòng) Django 服務(wù)后,我們使用 curl 命令發(fā)送 HTTP 請(qǐng)求如下:

# 準(zhǔn)備一個(gè)新的文件
[root@server ~]# cat upload_file.txt 
upload file test
[root@server ~]# curl -XPOST "http://127.0.0.1:8881/hello/?a=xxx&a=yyy&b=zzz" -F 'data={"name": "join", "age": 28}' -F "file=@/root/upload_file.txt"
request.scheme=http
request.body=b'------------------------------c28860e155fe\r\nContent-Disposition: form-data; name="data"\r\n\r\n{"name": "join", "age": 28}\r\n------------------------------c28860e155fe\r\nContent-Disposition: form-data; name="file"; filename="upload_file.txt"\r\nContent-Type: text/plain\r\n\r\nupload file test\n\r\n------------------------------c28860e155fe--\r\n'
request.path=/hello/
request.method=POST
request.GET=<QueryDict: {'a': ['xxx', 'yyy'], 'b': ['zzz']}>
request.FILES=<MultiValueDict: {'file': [<InMemoryUploadedFile: upload_file.txt (text/plain)>]}>
request.get_host=127.0.0.1:8881
request.get_port=8881
request.get_full_path=/hello/?a=xxx&a=yyy&b=zzz
request.get_raw_uri=http://127.0.0.1:8881/hello/?a=xxx&a=yyy&b=zzz
request.build_absolute_uri=http://127.0.0.1:8881/hello/?a=xxx&a=yyy&b=zzz

通過測(cè)試結(jié)果可以更容易理解 HttpRequest 類屬性和方法的含義。其中,上述 curl 請(qǐng)求中 -F 表示帶表單數(shù)據(jù)。

3. Django 視圖函數(shù)的返回值

對(duì)于視圖函數(shù)的返回值,往往有如下幾種方式:

3.1 直接返回字符串

直接返回字符串是非常常見的一種方式,不過我們需要將字符串作為參數(shù)傳到 HttpResponse 類中實(shí)例化后返回:

def hello_world(request, *args, **kwargs):
    return HttpResponse('要返回的字符串')

Tips:HttpResponse:是 Django 中封裝的用于返回響應(yīng)的類 。

3.2 返回 json 類型

視圖函數(shù)直接返回 json 數(shù)據(jù)是在微服務(wù)架構(gòu)中常見的套路。這里 Django 程序只提供后端數(shù)據(jù)并不提供靜態(tài)資源。針對(duì)返回 json 數(shù)據(jù),在 Django 中專門定義了一個(gè) JsonResponse 類用來生成 json 數(shù)據(jù)。它實(shí)際上是繼承自 HttpResponse 類:

# django/http/response.py

# 忽略其他內(nèi)容

class JsonResponse(HttpResponse):
    """
    忽略注釋部分內(nèi)容
    """
    def __init__(self, data, encoder=DjangoJSONEncoder, safe=True,
                 json_dumps_params=None, **kwargs):
        if safe and not isinstance(data, dict):
            raise TypeError(
                'In order to allow non-dict objects to be serialized set the '
                'safe parameter to False.'
            )
        if json_dumps_params is None:
            json_dumps_params = {}
        kwargs.setdefault('content_type', 'application/json')
        data = json.dumps(data, cls=encoder, **json_dumps_params)
        super().__init__(content=data, **kwargs)

JsonResponse 類的使用和 HttpResponse 類一樣簡(jiǎn)單,我們只需要把字典數(shù)據(jù)傳給 JsonResponse 類進(jìn)行實(shí)例化即可。但是字典數(shù)據(jù)中存在中文時(shí)候,會(huì)出現(xiàn)亂碼,我們只需要在實(shí)例化 JsonResponse 時(shí),多傳入一個(gè)參數(shù)即可:

# 在頁(yè)面上會(huì)出現(xiàn)亂碼
def hello_world(request, *args, **kwargs):
    data = {'code': 0, "content": "返回中文字符串", "err_msg": ""}
    return JsonResponse(data)

# 經(jīng)過處理后的JsonResponse
def hello_world(request, *args, **kwargs):
    data = {'code': 0, "content": "返回中文字符串", "err_msg": ""}
    return JsonResponse(data, json_dumps_params={'ensure_ascii': False})

請(qǐng)求結(jié)果:

# 第一個(gè)不處理的 JsonResponse 返回
[root@server ~]# curl "http://127.0.0.1:8881/hello/"
{"code": 0, "content": "\u8fd4\u56de\u4e2d\u6587\u5b57\u7b26\u4e32", "err_msg": ""}

# 使用第二個(gè)數(shù)據(jù)處理后的HttpResponse返回
[root@server ~]# curl "http://127.0.0.1:8881/hello/"
{"code": 0, "content": "返回中文字符串", "err_msg": ""}

另外一種比較好的方式是,仿照 JsonResponse 類,定義一個(gè)支持返回包含中文 json 數(shù)據(jù)的 Response 類:

# 忽略導(dǎo)入模塊

# 將原來支持的json_dumps_params參數(shù)固定寫死成{'ensure_ascii':False}
class JsonResponseCn(HttpResponse):
    """
    忽略注釋部分內(nèi)容
    """
    def __init__(self, data, encoder=DjangoJSONEncoder, safe=True, **kwargs):
        if safe and not isinstance(data, dict):
            raise TypeError(
                'In order to allow non-dict objects to be serialized set the '
                'safe parameter to False.'
            )
        kwargs.setdefault('content_type', 'application/json')
        data = json.dumps(data, cls=encoder, **{'ensure_ascii':False})
        super().__init__(content=data, **kwargs)

這樣處理后,我們?cè)诤驮瓉硪粯邮褂?JsonResponseCn 類來返回 json 數(shù)據(jù)即可,不用考慮返回的字典數(shù)據(jù)中是否包含中文字符:

def hello_world(request, *args, **kwargs):
    data = {'code': 0, "content": "返回中文字符串", "err_msg": ""}
    return JsonResponseCn(data)

3.3 調(diào)用 render 函數(shù)返回

HTML 文本或者模板文件。其實(shí),通過查看 Django 的源代碼,可以看到 render 函數(shù)會(huì)調(diào)用 loader.render_to_string() 方法將 html 文件轉(zhuǎn)成 string,然后作為 content 參數(shù)傳遞給 HttpResponse 類進(jìn)行實(shí)例化:

# django/shortcuts.py

def render(request, template_name, context=None, content_type=None, status=None, using=None):
    """
    Return a HttpResponse whose content is filled with the result of calling
    django.template.loader.render_to_string() with the passed arguments.
    """
    content = loader.render_to_string(template_name, context, request, using=using)
    return HttpResponse(content, content_type, status)

它的用法如下:

# 第一步,在django工程中準(zhǔn)備一個(gè)靜態(tài)文件,放到templates目錄下
(django-manual) [root@server first_django_app]# cat templates/index.html 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>這是首頁(yè)</h1>
</body>
</html>

# 第二步,在first_django_app/setting.py文件,指定靜態(tài)資源的目錄
(django-manual) [root@server first_django_app]# cat first_django_app/settings.py
...

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        # 指定項(xiàng)目目錄下的templates目錄
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

...

# 第三步,添加視圖函數(shù)以及URLconf,位置first_django_app/urls.py

def index(request, *args, **kwargs):
    return render(request, "index.html")

urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', index),
]

就這樣一個(gè)簡(jiǎn)單的配置,我們請(qǐng)求 /index/ 路徑時(shí),會(huì)返回 index.html 文件內(nèi)容:

[root@server ~]# curl "http://127.0.0.1:8881/index/"
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>這是首頁(yè)</h1>
</body>
</html>

另一方面,index.html 還可以是一個(gè)模板文件,我們通過 render 函數(shù)最后會(huì)將該模板文件轉(zhuǎn)成一個(gè)完整的 HTML 文件,返回給客戶端:

# index.html
(django-manual) [root@server first_django_app]# cat templates/index.html 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>{{ title }}</h1>
<p>{{ content }}</p>
</body>
</html>

# 修改 urls.py 中的視圖函數(shù)
(django-manual) [root@server first_django_app]# cat first_django_app/urls.py
...

def index(request, *args, **kwargs):
    return render(request, "index.html", {"title":"首頁(yè)", "content": "這是正文"})
    
...

最后請(qǐng)求結(jié)果可以看到完整的 HTML 文本:

[root@server ~]# curl "http://127.0.0.1:8881/index/"
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>首頁(yè)</h1>
<p>這是正文</p>
</body>
</html>

3.4 調(diào)用 redirect 函數(shù)

實(shí)現(xiàn)重定向功能,它的常用形式如下:

from django.shortcuts import redirect

# 指定完整的網(wǎng)址
def redirect_view1(request):
    # 忽略其他
    return redirect("http://www.baidu.com")

# 指定內(nèi)部path路徑
def redirect_view2(request):
    # 忽略其他
  return redirect("/index/")

def redirect_view3(request):
    # 忽略其他
    return redirect(reverse('blog:article_list'))

def redirect_view4(request):
    # 忽略其他
    return redirect('some-view-name', foo='bar')

4. 給視圖加裝飾器

在 Django 工程中,由于視圖有兩種:FBV 和 CBV,因此視圖加裝飾器的情況也分為兩種:給視圖函數(shù)加裝飾器和給視圖類加裝飾器。由于視圖函數(shù)是普通的 Python 函數(shù),因此給視圖函數(shù)加裝飾器和給普通函數(shù)加裝飾器方式一致。下面代碼中我們給視圖函數(shù) index 加了一個(gè)簡(jiǎn)單的裝飾器,用于在執(zhí)行視圖函數(shù)前和后各打印一段字符:

from django.shortcuts import render, HttpResponse, redirect

def wrapper(f):
    def innser(*args, **kwargs):
        print('before')
        ret = f(*args, **kwargs)
        print('after')
        return ret
    return innser
 
@wrapper
def index(request):
  return render(request, 'index.html')

由于類中的方法與普通函數(shù)不完全相同,因此不能直接將函數(shù)裝飾器應(yīng)用于類中的方法 ,我們需要先將其轉(zhuǎn)換為方法裝飾器。Django 中提供了 method_decorator 裝飾器用于將函數(shù)裝飾器轉(zhuǎn)換為方法裝飾器:

# 定義函數(shù)裝飾器
def wrapper(f):
    def innser(*args, **kwargs):
        print('before')
        ret = f(*args, **kwargs)
        print('after')
        return ret
    return innser


# 另一個(gè)代碼文件中使用wrapper裝飾器
from django.views import View
from django.utils.decorators import method_decorator

@method_decorator(wrapper, name='get')
class HelloView(View):
    # @method_decorator(wrapper)
    def get(self, request, *args, **kwargs):  
        print('get')
        return HttpResponse('get\n')
      
    def post(self, request, *args, **kwargs):
        print('post')
        return HttpResponse('post\n')
    
    def put(self, request, *args, **kwargs): 
        print('put')
        return HttpResponse('put\n')
    
    def delete(self, request, *args, **kwargs): 
        print('delete')
        return HttpResponse('delete\n')
    
    @csrf_exempt
    # @method_decorator(wrapper)
    def dispatch(self, request, *args, **kwargs):
        return super(HelloView, self).dispatch(request, *args, **kwargs)

對(duì)于給 View 類添加裝飾器,我們有如下幾種性質(zhì):

  • method_decorator 裝飾器直接添加到 View 類上。第一個(gè)參數(shù)指定需要添加的裝飾器,如 wrapper,name 參數(shù)可以選擇將裝飾器 wrapper 作用到 View 類中的哪個(gè)函數(shù)。給類添加裝飾器時(shí),必須要指定 name 參數(shù),不然運(yùn)行 Django 服務(wù)時(shí)會(huì)報(bào)錯(cuò);

  • method_decorator 裝飾器可以直接作用于 View 類中的函數(shù)上。如果是在 get、post 這類 HTTP 請(qǐng)求方法的函數(shù)上,則裝飾器只會(huì)作用于調(diào)用該函數(shù)的 http 請(qǐng)求上;

  • 如果 method_decorator 裝飾器作用于 dispatch 函數(shù)上。由于對(duì)于視圖中所有的 HTTP 請(qǐng)求都會(huì)先調(diào)用 dispatch 方法找到對(duì)應(yīng)請(qǐng)求方法的函數(shù),然后再執(zhí)行。所以這樣對(duì)應(yīng)視圖內(nèi)的所有 HTTP 請(qǐng)求方式都先經(jīng)過裝飾器函數(shù)處理;

5. 小結(jié)

本小節(jié)中,我們介紹了視圖的兩種形式 FBV 和 CBV,并進(jìn)行了簡(jiǎn)單的說明。接下來我們?cè)敿?xì)描述了 Django 中的 HttpRequest 和 HttpResponse 對(duì)象,并進(jìn)行了代碼說明。最后我們介紹了給 FBV 和 CBV 加上裝飾器的方法。