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

首頁 慕課教程 Django 入門教程 Django 入門教程 10 Django 中傳遞參數給視圖函數

Django 中傳遞參數給視圖函數

Django 框架中推薦使用一個單獨的 python 模塊配置 URL 和視圖函數或者視圖類的映射關系,通常稱這個配置模塊為 URLconf,該 Python 模塊通常命名為 urls.py。一般而言,每個應用目錄下都會有一個 urls.py 文件,總的映射關系入口在項目目錄下的 urls.py 中,而這個位置又是在 settings.py 文件中指定的。

本小節(jié)中將會學習 Django 中的路由系統(tǒng)、URLconf 的配置,以及如何將請求參數,如表單數據、文件等傳遞給視圖函數。

1. 測試環(huán)境準備

這里的實驗環(huán)境會采用前面創(chuàng)建的第一個 Django 工程(first_django_app) 來進行測試。在 first_django_app 中,我們創(chuàng)建了第一個 django app 應用:hello_app。現在按照如下步驟準備實驗環(huán)境:

在 hello_app 應用目錄下的 views.py 中添加一個視圖函數:

from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.

def hello_view(request, *args, **kwargs):
    return HttpResponse('hello, world!')

在 hello_app 應用目錄下新建 urls.py 文件,里面內容如下:

from django.urls import path

from . import views

urlpatterns = [
    # 資產查詢接口,根據我們自己的機器命名
    path('world/', views.hello_view, name='hello_view'),
]

settings.py 文件中注冊應用:

# first_django_app/settings.py
...

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # 注冊應用
    'hello_app'
]

...

在 URLconf 的總入口位置使用 django 提供的 include 方法將應用中的 urls.py 中的 urlpatterns 添加進來:

from django.contrib import admin
from django.conf.urls import include, url

# 所有url入口
urlpatterns = [
    url('admin/', admin.site.urls),
    url('hello/', include('hello_app.urls')),
]

這樣之后,我們請求地址 /hello/world/ 時,就能看到頁面顯示 “hello, world!” 字符了。后面的測試將在應用的 urls.pyviews.py 中進行。

2. Django 中的傳參方式

Django 中傳遞參數給視圖函數的方式主要可分為以下兩種形式:URL 傳參和非 URL 傳參兩種。第一種基于 Django 中的 URLconf 配置,可以通過 URL 路徑將對應匹配的參數傳到視圖函數中;而另外一種就是屬于HTTP 請求攜帶的參數了,請求參數可以放到 URL 中以 ? 格式加到 URL 的最后面,也可以將參數放到請求 body 中,最后統(tǒng)一由視圖函數中的 request 參數保存并傳到視圖函數中。

2.1 動態(tài) URL 傳參

在 url 的路徑 (path)部分可以作為動態(tài)參數,傳遞給視圖函數,如下面幾種寫法:

# hello_app/urls.py
from django.urls import path

from . import views

urlpatterns = [
    path('articles/<int:year>/', views.year_archive),
    path('articles/<int:year>/<int:month>/', views.month_archive),
    path('articles/<int:year>/<int:month>/<slug:title>/', views.article_title),
]

注意上面的定義了匹配三個動態(tài) URL 的映射,每個動態(tài) URL 會匹配一個至多個參數,每個動態(tài)值使用 <> 符號匹配,采用 <type:name> 這樣的形式。我們對應的視圖函數如下:

from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.

def year_archive(request, year, *args, **kwargs):
    return HttpResponse('hello, {} archive\n'.format(year))
    
def month_archive(request, year, month, *args, **kwargs):
    return HttpResponse('hello, month archive, year={}, moth={}!\n'.format(year, month))

def article_title(request, year, month, title, *args, **kwargs):
    return HttpResponse('hello, title archive, year={}, month={}, title={}!\n'.format(year, month, title))

對于動態(tài)的 URL 表達式中,匹配到的值,比如上面的 year,month 和 title 可以作為函數的參數放到對應的視圖函數中,Django 會幫我們把匹配到的參數對應的放到函數的參數上。這里參數的位置可以任意寫,但是名字必須和 URL 表達式中的對應。

[root@server first_django_app]# curl http://127.0.0.1:8881/hello/articles/1998/
hello, 1998 archive
[root@server first_django_app]# curl http://127.0.0.1:8881/hello/articles/1998/12/
hello, month archive, year=1998, moth=12!
[root@server first_django_app]# curl http://127.0.0.1:8881/hello/articles/1998/12/test/
hello, title archive, year=1998, month=12, title=test

比如 URL 中有 3 個動態(tài)參數,在視圖函數中只寫上兩個參數接收也是沒問題的,因為剩下的參數會被傳到 kwargs 中以 key-value 的形式保存:

(django-manual) [root@server first_django_app]# cat hello_app/views.py
...

def article_title(request, year, month, *args, **kwargs):
    return HttpResponse('hello, title archive, year={}, month={}, kwargs={}\n'.format(year, month, kwargs))

# 啟動服務,再次請求后
[root@server first_django_app]# curl http://127.0.0.1:8881/hello/articles/1998/12/test/
hello, title archive, year=1998, month=12, kwargs={'title': 'test'}

上述介紹的動態(tài) URL 匹配格式 <type:name> 中,Django 會對捕捉到的 URL 參數進行強制類型裝換,然后賦給 name 變量,再傳到視圖函數中。其中 Django 框架中支持的轉換類型有:

  • str:匹配任意非空字符,不能匹配分隔符 “/”;

  • int:匹配任意大于0的整數;

  • slug:匹配任意 slug 字符串, slug 字符串可以包含任意的 ASCII 字符、數字、連字符和下劃線等;

  • uuid:匹配 UUID 字符串;

  • path:匹配任意非空字符串,包括 URL 的分隔符 “/”。

2.2 自定義URL參數類型轉換器

除了 Django 定義的簡單類型,我們還可以自定義參數類型轉換器來支持更為復雜的 URL 場景。比如前面的 int 類型并不支持負整數,我希望開發(fā)一個能匹配正負數的類型,具體的步驟如下:

hello_app/urls.py 中定義一個 SignInt 類。該類有一個固定屬性 regex,用于匹配動態(tài)值;兩個固定方法:to_python() 方法和 to_url() 方法:

# hello_app/urls.py

class SignInt:
    regex = '-*[0-9]+'

    def to_python(self, value):
        # 將匹配的value轉換成我們想要的類型
        return int(value)

    def to_url(self, value):
        # 反向生成url時候回用到
        return value

注冊該定義的轉換器,并給出一個簡短的名字,比如 sint:

# hello_app/urls.py

from django.urls import converters, register_converter

register_converter(SignInt, 'sint')

...

最后,我們就可以在 URLconf 中使用該類型來配置動態(tài)的 URL 表達式:

# hello_app/urls.py

urlpatterns = [
    path('articles/<sint:signed_num>/', views.signed_convert),
]

# hello_app/views.py
def signed_convert(request, signed_num, **kwargs):
    return HttpResponse('hello, 自定義類型轉換器,獲取參數={}\n'.format(signed_num))

啟動 Django 服務后,執(zhí)行相關請求,可以看到 Django 的路由系統(tǒng)能成功匹配到帶負數和正數的 URL:

[root@server ~]# curl http://127.0.0.1:8881/hello/articles/1998/
hello, 自定義類型轉換器,獲取參數=1998
[root@server ~]# curl http://127.0.0.1:8881/hello/articles/-1998/
hello, 自定義類型轉換器,獲取參數=-1998

2.3 使用正則表達式

上面是比較簡單的 URLconf 配置形式,Django 框架中可以使用正則表達式來進一步擴展動態(tài) URL 的配置,此時 urlpatterns 中的不再使用 path 方法而是支持正則表達式形式的 re_path 方法。此外,在 Python 的正則表達式中支持對匹配結果進行重命名,語法格式為:(?P<name>pattern),其中 name 為該匹配的名稱,pattern 為匹配的正則表達式。 這樣我們可以有如下的 URLconf 配置:

# hello_app/urls.py
from django.urls import re_path

from . import views

urlpatterns = [
    re_path('articles/(?P<year>[0-9]{4})/', views.year_archive),
    re_path('articles/(?P<year>[0-9]{4})/(?P<month>0[1-9]|1[0-2])/', views.month_archive),
    re_path('articles/(?P<year>[0-9]{4})/(?P<month>0[1-9]|1[0-2])/(?P<title>[a-zA-Z0-9-_]+)/', views.article_title),
]

注意:這里使用正則表達式的 URL 匹配和前面的普通的動態(tài) URL 匹配有一個非常重要的區(qū)別,基于正則表達式的URL 匹配一旦匹配成功就會直接跳轉到視圖函數進行處理,而普通的動態(tài) URL 匹配則會找到最長匹配的動態(tài) URL,然后再進入相應的視圖函數去處理:

[root@server ~]# curl http://127.0.0.1:8881/hello/articles/1998/12/test
hello, 1998 archive

可以看到,這里并沒有匹配到第三個 re_path 的 URL 配置,而是直接由第一個 re_path 的視圖函數進行了處理。

2.4 URLconf 傳遞額外參數

在前面的 URLconf 配置中,我們的 re_path 方法中只傳遞兩個參數,分別是設計的路由以及對應的視圖函數。我們可以看看 Django-2.2.10 中的 path 和 re_path 方法的源代碼:

# django/urls/conf.py
# ...

def _path(route, view, kwargs=None, name=None, Pattern=None):
    if isinstance(view, (list, tuple)):
        # For include(...) processing.
        pattern = Pattern(route, is_endpoint=False)
        urlconf_module, app_name, namespace = view
        return URLResolver(
            pattern,
            urlconf_module,
            kwargs,
            app_name=app_name,
            namespace=namespace,
        )
    elif callable(view):
        # view是函數
        pattern = Pattern(route, name=name, is_endpoint=True)
        return URLPattern(pattern, view, kwargs, name)
    else:
        raise TypeError('view must be a callable or a list/tuple in the case of include().')


path = partial(_path, Pattern=RoutePattern)
re_path = partial(_path, Pattern=RegexPattern)

可以看到,除了route 和 view 外,我們還有 name、kwargs、Pattern 參數(比較少用)。其中 name 參數表示的是 route 匹配到的 URL 的一個別名,而 kwargs 是我們可以額外傳給視圖函數的參數:

# hello_app/urls.py
...

urlpatterns = [
    re_path('articles/(?P<year>[0-9]{4})/', views.year_archive, {'hello': 'app'}),
]

# hello_app/views.py
def year_archive(request, *args, **kwargs):
    return HttpResponse('hello, year archive, 額外參數={}\n'.format(kwargs))

啟動 Django 服務后,我們請求對應的服務,可以看到除了 URL 中匹配的 year 參數外,還有 re_path 中額外傳遞的參數,最后都被視圖函數中的 **kwargs 接收:

[root@server ~]# curl http://127.0.0.1:8881/hello/articles/1998/
hello, year archive, 額外參數={'year': '1998', 'hello': 'app'}

2.5 從 HttpRequest 中獲取參數

從 HttpRequest 中獲取參數是我們進行 Web 開發(fā)中最常用的一種方式。對于 Django 的視圖函數來說,HTTP 請求的數據被 HttpRequest 實例化后傳到了視圖函數的第一個參數中。為了能觀察相關信息,我們修改請求的視圖函數:

@csrf_exempt
def hello_view(request, *args, **kwargs):
    # 在第三次使用表單上傳包括文件數據時,需要request.GET和request.POST操作,不然會拋異常
    params = "request.GET={}\n".format(request.GET)
    params += "request.POST={}\n".format(request.POST)
    params += "request.body={}\n".format(request.body)
    params += "request.FILES={}\n".format(request.FILES)

    return HttpResponse(params)

我們測試如下 3 種 HTTP 請求,分別為 GET 請求、POST 請求 和帶文件參數的請求,結果如下:

[root@server ~]# curl -XGET "http://127.0.0.1:8881/hello/world/?a=xxxx&b=yyyy" 
request.GET=<QueryDict: {'a': ['xxxx'], 'b': ['yyyy']}>
request.POST=<QueryDict: {}>
request.body=b''
request.FILES=<MultiValueDict: {}>

[root@server ~]# curl -XPOST -d "username=shen&password=shentong" "http://127.0.0.1:8881/hello/world/?a=xxxx&b=yyyy" 
request.GET=<QueryDict: {'a': ['xxxx'], 'b': ['yyyy']}>
request.POST=<QueryDict: {'username': ['shen'], 'password': ['shentong']}>
request.body=b'username=shen&password=shentong'
request.FILES=<MultiValueDict: {}>

# 本次請求中,需要去掉request.GET和request.POST操作語句,不然請求會報錯
[root@server ~]# curl -XPOST -F "username=shen&password=shentong" "http://127.0.0.1:8881/hello/world/?a=xxxx&b=yyyy" -F "files=@/root/upload_file.txt"
request.body=b'------------------------------68c9ede00e93\r\nContent-Disposition: form-data; name="username"\r\n\r\nshen&password=shentong\r\n------------------------------68c9ede00e93\r\nContent-Disposition: form-data; name="files"; filename="upload_file.txt"\r\nContent-Type: text/plain\r\n\r\nupload file test\n\r\n------------------------------68c9ede00e93--\r\n'
request.FILES=<MultiValueDict: {'files': [<InMemoryUploadedFile: upload_file.txt (text/plain)>]}>

可以看到,跟在 “?” 后的參數數據會保存到 request.GET 中,這也是 GET 請求帶參數的方式。對于 POST 請求的傳參,數據一般會保存在 request.POSTrequest.body 中。對于最后發(fā)送的上傳文件請求,可以看到,文件內容的內容數據是保存到了 request.body 中。

3. 小結

本節(jié)內容我們主要介紹了如何向視圖函數傳送參數,包括兩種方式:

  1. 通過動態(tài)的 URLconf 配置傳遞參數到視圖函數中;

  2. 通過 http 請求帶參數傳遞給視圖函數。至此,Django 中的路由系統(tǒng)和視圖函數已經介紹完畢。接下來會介紹 Django 中的模板系統(tǒng)。