當(dāng)前位置:首頁 > > HelloGitHub

作者:HelloGitHub-追夢(mèng)人物

文中涉及的示例代碼,已同步更新到?HelloGitHub-Team 倉(cāng)庫[1]?

點(diǎn)擊本文最下方的“閱讀原文”即可獲取

Web 服務(wù)簡(jiǎn)單的說就是處理請(qǐng)求,每個(gè)請(qǐng)求就像是一個(gè)“顧客”。首先熱情地把顧客迎接進(jìn)來,然后滿足用戶的個(gè)性化需求,最后讓顧客心滿意足的離開。Django 作為一個(gè) web 框架,能夠讓開發(fā)者有更多的精力和時(shí)間去應(yīng)付復(fù)雜多變的需求,而不是把時(shí)間花費(fèi)在招店小二、做飯的廚子、服務(wù)員等。那么下面我們就來看看 Django 的接客之道吧。

Django 處理 HTTP 請(qǐng)求

Web 應(yīng)用的交互過程其實(shí)就是 HTTP 請(qǐng)求與響應(yīng)的過程。無論是在 PC 端還是移動(dòng)端,我們通常使用瀏覽器來上網(wǎng),上網(wǎng)流程大致來說是這樣的:

1.我們打開瀏覽器,在地址欄輸入想訪問的網(wǎng)址,比如 https://zmrenwu.com/(當(dāng)然你也可能從收藏夾里直接打開網(wǎng)站,但本質(zhì)上都是一樣的)。2.瀏覽器知道我們想要訪問哪個(gè)網(wǎng)址后,它在后臺(tái)幫我們做了很多事情。主要就是把我們的訪問意圖包裝成一個(gè) HTTP 請(qǐng)求,發(fā)給我們想要訪問的網(wǎng)址所對(duì)應(yīng)的服務(wù)器。通俗點(diǎn)說就是瀏覽器幫我們通知網(wǎng)站的服務(wù)器,說有人來訪問你啦,訪問的請(qǐng)求都寫在 HTTP 報(bào)文里了,你按照要求處理后告訴我,我再幫你回應(yīng)他!3.服務(wù)器處理了HTTP 請(qǐng)求,然后生成一段 HTTP 響應(yīng)給瀏覽器。瀏覽器解讀這個(gè)響應(yīng),把相關(guān)的內(nèi)容在瀏覽器里顯示出來,于是我們就看到了網(wǎng)站的內(nèi)容。比如你訪問了我的博客主頁 https://zmrenwu.com/,服務(wù)器接收到這個(gè)請(qǐng)求后就知道用戶訪問的是首頁,首頁顯示的是全部文章列表,于是它從數(shù)據(jù)庫里把文章數(shù)據(jù)取出來,生成一個(gè)寫著這些數(shù)據(jù)的 HTML 文檔,包裝到 HTTP 響應(yīng)里發(fā)給瀏覽器,瀏覽器解讀這個(gè)響應(yīng),把 HTML 文檔顯示出來,我們就看到了文章列表的內(nèi)容。

因此,django 作為一個(gè) Web 框架,它的使命就是處理流程中的第二步。即接收瀏覽器發(fā)來的 HTTP 請(qǐng)求,返回相應(yīng)的 HTTP 響應(yīng)。于是引出這么幾個(gè)問題:

1.django 如何接收 HTTP 請(qǐng)求?2.django 如何處理這個(gè) HTTP 請(qǐng)求?3.django 如何生成 HTTP 響應(yīng)?

對(duì)于如何處理這些問題,django 有其一套規(guī)定的機(jī)制。我們按照 django 的規(guī)定,就能開發(fā)出所需的功能。

Hello 視圖函數(shù)

我們先以一個(gè)最簡(jiǎn)單的 Hello World 為例來看看 django 處理上述問題的機(jī)制是怎么樣的。

綁定 URL 與視圖函數(shù)

首先 django 需要知道當(dāng)用戶訪問不同的網(wǎng)址時(shí),應(yīng)該如何處理這些不同的網(wǎng)址(即所說的路由)。django 的做法是把不同的網(wǎng)址對(duì)應(yīng)的處理函數(shù)寫在一個(gè) urls.py 文件里,當(dāng)用戶訪問某個(gè)網(wǎng)址時(shí),django 就去會(huì)這個(gè)文件里找,如果找到這個(gè)網(wǎng)址,就會(huì)調(diào)用和它綁定在一起的處理函數(shù)(叫做視圖函數(shù))。

下面是具體的做法,首先在 blog 應(yīng)用的目錄下創(chuàng)建一個(gè) urls.py 文件,這時(shí)你的目錄看起來是這樣:

blog\    __init__.py    admin.py    apps.py    migrations\        0001_initial.py        __init__.py    models.py    tests.py    views.py    urls.py

在 blog\urls.py 中寫入這些代碼:

from django.urls import path
from . import views
urlpatterns = [ path('', views.index, name='index'),]

我們首先從 django.urls 導(dǎo)入了?path?函數(shù),又從當(dāng)前目錄下導(dǎo)入了 views 模塊。然后我們把網(wǎng)址和處理函數(shù)的關(guān)系寫在了?urlpatterns?列表里。

綁定關(guān)系的寫法是把網(wǎng)址和對(duì)應(yīng)的處理函數(shù)作為參數(shù)傳給?path?函數(shù)(第一個(gè)參數(shù)是網(wǎng)址,第二個(gè)參數(shù)是處理函數(shù)),另外我們還傳遞了另外一個(gè)參數(shù)?name,這個(gè)參數(shù)的值將作為處理函數(shù)?index?的別名,這在以后會(huì)用到。

注意這里我們的網(wǎng)址實(shí)際上是一個(gè)規(guī)則,django 會(huì)用這個(gè)規(guī)則去匹配用戶實(shí)際輸入的網(wǎng)址,如果匹配成功,就會(huì)調(diào)用其后面的視圖函數(shù)做相應(yīng)的處理。

比如說我們本地開發(fā)服務(wù)器的域名是 http://127.0.0.1:8000,那么當(dāng)用戶輸入網(wǎng)址 http://127.0.0.1:8000 后,django 首先會(huì)把協(xié)議 http、域名 127.0.0.1 和端口號(hào) 8000 去掉,此時(shí)只剩下一個(gè)空字符串,而?''?的模式正是匹配一個(gè)空字符串,于是二者匹配,django 便會(huì)調(diào)用其對(duì)應(yīng)的?views.index?函數(shù)。

注意

在 blogproject\ 目錄下(即 settings.py 所在的目錄),原本就有一個(gè) urls.py 文件,這是整個(gè)工程項(xiàng)目的 URL 配置文件。而我們這里新建了一個(gè) urls.py 文件,且位于 blog 應(yīng)用下。這個(gè)文件將用于 blog 應(yīng)用相關(guān)的 URL 配置,這樣便于模塊化管理。不要把兩個(gè)文件搞混了。

編寫視圖函數(shù)

第二步就是要實(shí)際編寫我們的?views.index?視圖函數(shù)了,按照慣例視圖函數(shù)定義在 views.py 文件里:

blog/views.py
from django.http import HttpResponse
def index(request): return HttpResponse("歡迎訪問我的博客首頁!")

我們前面說過,Web 服務(wù)器的作用就是接收來自用戶的 HTTP 請(qǐng)求,根據(jù)請(qǐng)求內(nèi)容作出相應(yīng)的處理,并把處理結(jié)果包裝成 HTTP 響應(yīng)返回給用戶。

這個(gè)兩行的函數(shù)體現(xiàn)了這個(gè)過程。它首先接受了一個(gè)名為?request?的參數(shù),這個(gè)request?就是 django 為我們封裝好的 HTTP 請(qǐng)求,它是類?HttpRequest?的一個(gè)實(shí)例。然后我們便直接返回了一個(gè) HTTP 響應(yīng)給用戶,這個(gè) HTTP 響應(yīng)也是 django 幫我們封裝好的,它是類?HttpResponse?的一個(gè)實(shí)例,只是我們給它傳了一個(gè)自定義的字符串參數(shù)。

瀏覽器接收到這個(gè)響應(yīng)后就會(huì)在頁面上顯示出我們傳遞的內(nèi)容 :歡迎訪問我的博客首頁!

配置項(xiàng)目 URL

還差最后一步了,我們前面建立了一個(gè) urls.py 文件,并且綁定了 URL 和視圖函數(shù)?index,但是 django 并不知道。django 匹配 URL 模式是在 blogproject\ 目錄(即 settings.py 文件所在的目錄)的 urls.py 下的,所以我們要把 blog 應(yīng)用下的 urls.py 文件包含到 blogproject\urls.py 里去,打開這個(gè)文件看到如下內(nèi)容:

blogproject/urls.py
"""一大段注釋"""
from django.contrib import adminfrom django.urls import path
urlpatterns = [ path('admin/', admin.site.urls),]

修改成如下的形式:

from django.contrib import adminfrom django.urls import path, include
urlpatterns = [ path('admin/', admin.site.urls), path('', include('blog.urls')),]

我們這里導(dǎo)入了一個(gè)?include?函數(shù),然后利用這個(gè)函數(shù)把 blog 應(yīng)用下的 urls.py 文件包含了進(jìn)來。此外 include 前還有一個(gè)?'',這是一個(gè)空字符串。這里也可以寫其它字符串,django 會(huì)把這個(gè)字符串和后面 include 的 urls.py 文件中的 URL 拼接。比如說如果我們這里把?''?改成?'blog/',而我們?cè)?blog\urls.py 中寫的 URL 是?'',即一個(gè)空字符串。那么 django 最終匹配的就是 blog/ 加上一個(gè)空字符串,即 blog/。

運(yùn)行結(jié)果

運(yùn)行?pipenv run python manage.py runserver?打開開發(fā)服務(wù)器,在瀏覽器輸入開發(fā)服務(wù)器的地址 http://127.0.0.1:8000/,可以看到 django 返回的內(nèi)容了。

歡迎訪問我的博客首頁!

使用 django 模板系統(tǒng)

這基本上就上 django 的開發(fā)流程了,寫好處理 HTTP 請(qǐng)求和返回 HTTP 響應(yīng)的視圖函數(shù),然后把視圖函數(shù)綁定到相應(yīng)的 URL 上。

但是等一等!我們看到在視圖函數(shù)里返回的是一個(gè)?HttpResponse?類的實(shí)例,我們給它傳入了一個(gè)希望顯示在用戶瀏覽器上的字符串。但是我們的博客不可能只顯示這么一句話,它有可能會(huì)顯示很長(zhǎng)很長(zhǎng)的內(nèi)容。比如我們發(fā)布的博客文章列表,或者一大段的博客文章。我們不能每次都把這些大段大段的內(nèi)容傳給?HttpResponse。

django 對(duì)這個(gè)問題給我們提供了一個(gè)很好的解決方案,叫做模板系統(tǒng)。django 要我們把大段的文本寫到一個(gè)文件里,然后 django 自己會(huì)去讀取這個(gè)文件,再把讀取到的內(nèi)容傳給?HttpResponse。讓我們用模板系統(tǒng)來改造一下上面的例子。

首先在我們的項(xiàng)目根目錄(即 manage.py 文件所在目錄)下建立一個(gè)名為 templates 的文件夾,用來存放我們的模板。然后在 templates\ 目錄下建立一個(gè)名為 blog 的文件夾,用來存放和 blog 應(yīng)用相關(guān)的模板。

當(dāng)然模板存放在哪里是無關(guān)緊要的,只要 django 能夠找到的就好。但是我們建立這樣的文件夾結(jié)構(gòu)的目的是把不同應(yīng)用用到的模板隔離開來,這樣方便以后維護(hù)。我們?cè)?templates\blog 目錄下建立一個(gè)名為 index.html 的文件,此時(shí)你的目錄結(jié)構(gòu)應(yīng)該是這樣的:

HelloDjango-blog-tutorial\    manage.py     ...    templates\        blog\            index.html

注意

再一次強(qiáng)調(diào) templates\ 目錄位于項(xiàng)目根目錄,而 index.html 位于 templates\blog 目錄下,而不是 blog 應(yīng)用下,如果弄錯(cuò)了你可能會(huì)得到一個(gè)?TemplateDoesNotExist?異常。如果遇到這個(gè)異常,請(qǐng)回來檢查一下模板目錄結(jié)構(gòu)是否正確。

在 templates\blog\index.html 文件里寫入下面的代碼:

    {{ title }}

{{ welcome }}

這是一個(gè)標(biāo)準(zhǔn)的 HTML 文檔,只是里面有兩個(gè)比較奇怪的地方:{{ title }}{{ welcome }}。這是 django 規(guī)定的語法。用 {{ }} 包起來的變量叫做模板變量。django 在渲染這個(gè)模板的時(shí)候會(huì)根據(jù)我們傳遞給模板的變量替換掉這些變量。最終在模板中顯示的將會(huì)是我們傳遞的值。

注意:

index.html 必須以 UTF-8 的編碼格式保存,且小心不要往里面添加一些特殊字符,否則極有可能得到一個(gè)?UnicodeDecodeError?這樣的錯(cuò)誤。

模板寫好了,還得告訴 django 去哪里找模板,在 settings.py 文件里設(shè)置一下模板文件所在的路徑。在 settings.py 找到?TEMPLATES?選項(xiàng),它的內(nèi)容是這樣的:

blogproject/settings.py
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.djangoTemplates', 'DIRS': [], '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', ], }, },]

其中?DIRS?就是設(shè)置模板的路徑,在 [] 中寫入?os.path.join(BASE_DIR, 'templates'),即像下面這樣:

blogproject/settings.py
TEMPLATES = [ { ... 'DIRS': [os.path.join(BASE_DIR, 'templates')], ... },]

這里?BASE_DIR?是 settings.py 在配置開頭前面定義的變量,記錄的是工程根目錄 HelloDjango-blog-tutorial\ 的值。在這個(gè)目錄下有模板文件所在的目錄 templates\,于是利用os.path.join?把這兩個(gè)路徑連起來,構(gòu)成完整的模板路徑,django 就知道去這個(gè)路徑下面找我們的模板了。

視圖函數(shù)可以改一下了:

blog/views.py
from django.shortcuts import render

def index(request): return render(request, 'blog/index.html', context={ 'title': '我的博客首頁', 'welcome': '歡迎訪問我的博客首頁' })

這里我們不再是直接把字符串傳給?HttpResponse?了,而是調(diào)用 django 提供的?render?函數(shù)。這個(gè)函數(shù)根據(jù)我們傳入的參數(shù)來構(gòu)造?HttpResponse

我們首先把 HTTP 請(qǐng)求傳了進(jìn)去,然后?render?根據(jù)第二個(gè)參數(shù)的值 blog/index.html 找到這個(gè)模板文件并讀取模板中的內(nèi)容。之后?render?根據(jù)我們傳入的?context?參數(shù)的值把模板中的變量替換為我們傳遞的變量的值,{{ title }}?被替換成了?context?字典中?title?對(duì)應(yīng)的值,同理?{{ welcome }}?也被替換成相應(yīng)的值。

最終,我們的 HTML 模板中的內(nèi)容字符串被傳遞給?HttpResponse?對(duì)象并返回給瀏覽器(django 在?render?函數(shù)里隱式地幫我們完成了這個(gè)過程),這樣用戶的瀏覽器上便顯示出了我們寫的 HTML 模板的內(nèi)容了。

References

[1]?HelloGitHub-Team 倉(cāng)庫:?https://github.com/HelloGitHub-Team/HelloDjango-blog-tutorial


關(guān)注公眾號(hào)加入交流群,一起討論有趣的技術(shù)話題


HelloDjango 往期回顧:

第 04 篇:Django 遷移、操作數(shù)據(jù)庫

第 03 篇:創(chuàng)建 Django 博客的數(shù)據(jù)庫模型

第 02 篇:"空空如也"的博客應(yīng)用

第 01 篇:開始進(jìn)入 django 開發(fā)之旅


?點(diǎn)擊?“閱讀原文”?獲取代碼

?點(diǎn)擊?看”?支持我們

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
關(guān)閉