寫在前面:之前跟了實驗室的“緣游”項目,也是使用 django 后臺開發(fā)的档址。但是由于這個項目不是從頭搭建景东,是在之前的項目的基礎(chǔ)上面改出來的。所以呢對于整體流程的認(rèn)識并沒有那么清楚捆探。今天根據(jù) django 的官方文檔然爆,來搭建一個投票系統(tǒng),期望對整體的流程有一定的認(rèn)識黍图。
另外曾雕,看了看之前寫的 django 筆記
(來自自強(qiáng)學(xué)堂),覺得寫的太散了助被,所以剖张,要學(xué)一個新的技術(shù)或者框架,最好的方法還是動手寫一個開源項目揩环。
Notice
注意搔弄,首先要對 django 的版本選擇有一定的認(rèn)識。對于不同的版本丰滑,對應(yīng)不同的 python 版本對應(yīng)說明顾犹。
創(chuàng)建項目
在想要創(chuàng)建的位置用 命令行 進(jìn)行創(chuàng)建。
django-admin startproject mysite
可以看到在相應(yīng)的目錄下就創(chuàng)建了django工程。具體樹結(jié)構(gòu)如下圖炫刷。
- 最外層的 mysite/ 根目錄只是項目的容器擎宝,django 是不關(guān)心這個名字的,所以只要不產(chǎn)生命名沖突浑玛,這里可以隨便給
- manage.py: 命令行工具绍申。在之前做“緣游”的時候你也發(fā)現(xiàn)了,基本上都是manage.py 然后一些列操作顾彰。這個工具具體的細(xì)節(jié)功能可以參考官方文檔极阅。
- mysite/ 目錄包含了項目,是純 python 包拘央,引用該包內(nèi)的東西需要包含項目路徑涂屁,mysite.urls
- mysite/init.py:空文件,告訴 python 這個目錄應(yīng)該是一個純 python 包灰伟。
- mysite/settings.py:配置文件拆又。
- mysite/urls.py: URL聲明,就像是網(wǎng)站“目錄”
- mysite/wsgi.py:項目運(yùn)行在 WSGI 兼容的 Web 服務(wù)器上的入口栏账。(這個不懂是什么)
用于開發(fā)的簡易服務(wù)器
官方文檔在這一節(jié)的意思是告訴我們帖族,創(chuàng)建好項目之后,就可以啟動 django 自帶的建議服務(wù)器了挡爵。
python manage.py runserver [可以指定ip和端口]
官方在這里說明了竖般,這個輕量級的 Web 服務(wù)器只是為了開發(fā)用的。如果是用于生產(chǎn)等茶鹃,還是要配專業(yè)服務(wù)器 apache 什么的涣雕。
創(chuàng)建投票應(yīng)用
python manage.py startapp polls
創(chuàng)建 App。這時候就可以看到創(chuàng)建了app闭翩。
這里要理解 app 和 project 之間的關(guān)系挣郭,一個 pro 下面可以有多個 app。
這里面包含了投票應(yīng)用的全部內(nèi)容疗韵。
編寫第一個視圖
這里在 polls 應(yīng)用下的 view.py 視圖中兑障,編寫第一個函數(shù)。首先蕉汪,寫入一個 捕獲 request 請求并返回數(shù)據(jù)的方法流译,命名為 index;然后者疤,在 app 下創(chuàng)建一個 url.py 腳本來指明我們 app 的 URLconf福澡,將這個方法映射到相應(yīng)的 url 規(guī)則;最后驹马,要在 根URLconf 的 url.py 文件下指定我們創(chuàng)建的 app 的 URLconf 竞漾,這里使用 include() 方法眯搭。
這里官方文檔說明為什么要使用 include() 方法指明 app 中的 URLconf。
因為對于 投票應(yīng)用來說业岁,應(yīng)該有自己的 URLconf,但是其實 這個 polls/url.py 所寫的 URLconf寇蚊,應(yīng)該在其他的應(yīng)用下也可以使用笔时,處于這個考慮,才有了 include() 方法仗岸。
最終允耿,運(yùn)行服務(wù)器,可以發(fā)現(xiàn)這里的新寫的視圖產(chǎn)生了作用扒怖。
訪問 localhost:8000/polls 即可较锡。
數(shù)據(jù)庫配置
這里在 mysite/settings.py 下面進(jìn)行系列設(shè)置,這里我們采用 mysql 數(shù)據(jù)庫盗痒,填入 用戶名蚂蕴、密碼等。
-
migrate
這里有一些 django 自帶應(yīng)用在 settings 中是默認(rèn)開啟的俯邓,而這些應(yīng)用是需要一些數(shù)據(jù)表的骡楼,所以在這之前要在數(shù)據(jù)庫中創(chuàng)建一些相應(yīng)的表單,所以要執(zhí)行:
python manage.py migrate
這里的 migrate 命令就是說同步數(shù)據(jù)庫和當(dāng)前應(yīng)用二者的狀態(tài)(因為這里的默認(rèn)開啟的應(yīng)用在數(shù)據(jù)庫中是沒有表單的稽鞭,所以執(zhí)行 migrate操作之后會在相應(yīng)的 數(shù)據(jù)庫中 建立表單鸟整,如下圖)
-
makemigrations:
這個指令是用來生成遷移代碼的。
大白話講一下:每次改動 models.py之后朦蕴,先要makemigrations篮条,然后 migrate才能更改數(shù)據(jù)庫中的表單。
創(chuàng)建模型
其實在做一個 Web 應(yīng)用時候吩抓,第一步應(yīng)該是創(chuàng)建模型涉茧,就是說表單等數(shù)據(jù)庫的設(shè)計。
我們在這個投票應(yīng)用中琴拧,創(chuàng)建兩個模型:Question & Choice降瞳。
激活模型
就是說我們已經(jīng)更改過模型了,下一步就是讓模型對應(yīng)的數(shù)據(jù)庫表單生效蚓胸,也就是我們上面說過的 makemigrations & migrate兩步
這里官方文檔中還說了把 polls 添加進(jìn) 工程設(shè)置文件內(nèi)的 已安裝app 中挣饥。
如果沒有安裝,就不會檢測到 polls 中的任何變化沛膳。
個人理解扔枫,這些命令都是以 project 的層面執(zhí)行的。
初試API
這里官方文檔 講了 shell 接口
python manage.py shell
在這個接口下可以實現(xiàn)很多直接對數(shù)據(jù)庫的操作锹安。其實這些操作在可視化數(shù)據(jù)庫中更加便捷短荐。這里只需要知道有這么個功能就好了倚舀。
介紹 Django 管理頁面
這里講了一下創(chuàng)建一個超級用戶來進(jìn)入后臺管理界面。之后進(jìn)入管理界面忍宋。
不知道為什么半天沒有登陸進(jìn)去痕貌,但是最后啥也沒變就登錄進(jìn)去了。奇怪糠排。浪費(fèi)了很多時間舵稠。
向管理頁面中加入投票應(yīng)用
這時候在任何地方都看不到我們的投票應(yīng)用。
所以這個時候需要告訴管理入宦,問題 Question 對象需要被管理哺徊。 在 polls/admin.py 中進(jìn)行編輯。
from .models import Question
admin.site.register(Question)
之后就會看到這個 Question 類被添加到了后天管理頁面乾闰。
點(diǎn)擊Question之后可以看到之前生成的 Question 對象落追,點(diǎn)擊對象可以進(jìn)行編輯,這里編輯的 表單是 Django 根據(jù) Question 模型自動生成的涯肩。
真正開始編寫投票應(yīng)用
前面的可以說都是準(zhǔn)備工作轿钠,從這里開始我們才真正的開始。
首先需要明白視圖的概念:“一類具有相同功能和模板的網(wǎng)頁的集合”宽菜。在投票應(yīng)用中谣膳,我們需要以下幾個視圖:
- 問題索引頁:展示最近的幾個投票問題
- 問題詳情頁:問題和相應(yīng)的選項
- 問題結(jié)果頁:投票的結(jié)果
- 投票處理器:響應(yīng)投票操作
編寫更多的視圖
現(xiàn)在開始編寫上面對應(yīng)的視圖并且添加URL映射。
這里文檔中讓我們編寫了跟上面頁面對應(yīng)的4個視圖(包括最最開始的 index 視圖)铅乡,然后添加入 URLconf继谚。
這里添加了一個 ‘<int:question_id>’的匹配規(guī)則。
寫一個真正有用的視圖
Django 中要求一個視圖必須要 返回一個 HttpResponse 阵幸、或者拋出一個異常花履。
這里更改 index 這個view函數(shù),來顯示最近發(fā)布的5個問題挚赊。
之后會發(fā)現(xiàn)诡壁,index 頁面的設(shè)計是寫死在視圖函數(shù)的代碼里的。想要更改設(shè)計荠割,就要采用 templates妹卿。
在 poll/ 目錄下新建 templates 目錄,在 templates目錄下建立 polls 目錄(為了防止不同app之間重名的問題)蔑鹦,然后建立 index.html 腳本夺克。在腳本中寫入 html 樣式代碼,然后再回到視圖中的 index 函數(shù)中嚎朽,通過將模板內(nèi)的變量映射為 python 對象 來使用模板铺纽。
流程為: 載入模板,填充上下文哟忍,返回由模板生成的 HttpResponse 對象狡门。
同時 Django 還提供了一個快捷函數(shù) render() 來完成這個流程陷寝,就不需要 HttpResponse了。
這時候 索引頁面 index 已經(jīng)有了一點(diǎn)樣式了
拋出404錯誤
這里官方文檔給出了捕獲404異常從而給出 debug 的方法其馏,同時也給出了 get_object_or_404() 這個快捷函數(shù)凤跑。同樣可以起到 try: except 的作用。
去除模板中的硬編碼URL
這里是說 用 {% url %} 標(biāo)簽來通過 name 選項尋找 url叛复。這樣做可以在產(chǎn)生 url 變更時只需要在URLconf 中做相應(yīng)的更改饶火,只要不改變這里的名字,模板中就可以找到URL致扯。
為URL名稱添加命名空間
接上面,這里說到了重復(fù)名稱的問題当辐。為了區(qū)分不同app相同的 url 名抖僵。文檔給出了在 URLconf中加上 app_name 設(shè)置命名空間的方法。
編寫一個簡單的表單
文檔在這里給出了 detials.html 頁面的表單編寫方法缘揪。并指出了 但凡是調(diào)用 post 方法都是要更改后臺數(shù)據(jù)庫耍群,所以在 Django 中規(guī)定了 所有 Post 表單都要使用 {%csrf_token%}。 Post 的 view 函數(shù)也一樣找筝。
這里的 csrf 是一個驗證的方式蹈垢。
csrf 機(jī)制要求,發(fā)送 post/put/delete 請求的時候袖裕,是先以 get 方式發(fā)起一個請求曹抬,服務(wù)器分配一個隨機(jī)字符串給客戶端,之后客戶端攜帶這個 字符串再 發(fā)送請求急鳄。從而驗證身份是否合法谤民。
然后文檔給出了 投票提交頁面的視圖編寫方法。文檔指出疾宏,在處理 post 信息之后一定要重定向一個 URL张足,不然當(dāng)用戶點(diǎn)擊返回按鈕會重復(fù) post.
這里重新寫了 vote() 視圖等
使用通用視圖:代碼還是少點(diǎn)好
可以看到,我們之前 detial() results() 和 index() 視圖實現(xiàn)的功能都很類似:根據(jù) URL 中的參數(shù)從數(shù)據(jù)庫獲取數(shù)據(jù)坎藐、載入模板为牍、返回渲染后的模板。針對這種問題岩馍,django 提供一種叫做“通用視圖”的系統(tǒng)碉咆。
這里以投票 vote() 視圖為例子,將其轉(zhuǎn)換成使用通用視圖系統(tǒng):
- 轉(zhuǎn)換 URLconf
- 刪除舊的兼雄、不需要的視圖
- 基于 django 通用視圖 引入新的 視圖
改良 URLconf
這里首先在 URLconf 中進(jìn)行更改吟逝,使用通用視圖,首先要在 URLconf 中將視圖方法更改成為通用視圖方法赦肋。
修改視圖為通用視圖方法
然后需要更改視圖方法的代碼( 這里vote() 視圖方法不變的 )块攒。
# 使用通用視圖之前
def index(request):
late_list = Question.objects.order_by('-publish_date')[:5]
return render(request, 'polls/index.html', {'latest_ques_list': late_list})
def detail(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/detail.html', {'question': question})
def results(request,question_id):
question = get_object_or_404(Question, pk = question_id)
return render(request, 'polls/results.html', {'question': question})
# 使用通用視圖后
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
return Question.objects.order_by('-publish_date')[:5]
class DetailView(generic.DetailView):
model = Question
template_name = 'polls/detail.html'
class ResultsView(generic.DetailView):
model = Question
template_name = 'polls/results.html'
可以看到呢励稳,這里使用了兩個通用視圖 generic.ListView & generic.DetailView 視圖。
- 每個通用視圖需要知道它作用于哪個模型囱井。由 model 屬性提供
- DetailView 視圖期望從 URL 中捕獲 名為“pk”的主鍵值驹尼,所以把 URLconf 中的 question_id 該為 pk。
對于通用視圖庞呕,其有默認(rèn)的模板(如果我們不指定的話)新翎。比如 DetailView 默認(rèn)使用 <app name>/<model name>_detail.html 模板。而這里我們用 template_name 來指定其使用的模板住练。
這里所謂的通用視圖能夠減少代碼量體現(xiàn)在:
看使用通用視圖之前的代碼地啰,使用模板的時候(render()方法)都是需要提供 question 或者 latest_question_list 變量的context,而這些 變量是 需要我們手動寫代碼去獲取的讲逛。
但是亏吝,在通用視圖中,只需要我們提供 model 名盏混,比如這里的 DetailView 中 寫的 model = Question , 然后Django 就會自動的生成一個合適名字的 context蔚鸥。這就是所謂的 減少重復(fù)代碼 / 代碼少一點(diǎn)。
ListView 這個通用視圖和 DetailView有點(diǎn)不同许赃,這里我們除了 template_name 還提供了 context_object_name止喷,因為這里 Django 自動生成的context 變量是 question_list,而我們想用別的名字混聊,于是提供了一個覆蓋指令而已弹谁。注意這里 ListView 的model 名是寫在 get_queryset 中的(都是規(guī)定好的)。
這里的通用視圖技羔,就是說Django其實已經(jīng)提供好了一整套流程僵闯,只需要我們填入 model 和 templates 等,不需要再手動相關(guān)變量名了藤滥。
到了這里我們已經(jīng)把整個投票系統(tǒng)搭建完成了鳖粟。
下面的文檔都是教我們怎么樣來進(jìn)行測試和 debug 了。
自動化測試
這里文檔介紹了自動化測試對于整個項目的重要意義拙绊。并且指出向图,如果是一個用 django 找工作的人,他必須很會寫測試标沪。
開始寫我們的第一個測試
首先有一個bug
這里在之前開發(fā)的時候榄攀,其實是有一個 bug 的:Question.was_published_recently() 方法對一天前發(fā)布的問題是返回 True 的。 但是實際上來說金句,對于發(fā)布在未來時間的 對象檩赢,也是返回 True 的。這就是一個現(xiàn)有的 bug违寞。
可以在 shell 命令行里面試一下就會發(fā)現(xiàn)了贞瞒。
創(chuàng)建一個測試來 暴露這個 bug
其實剛剛我們剛剛在 shell 里面進(jìn)行這個 “試一下” 的過程其實就是在 測試偶房,不過這個是手動的測試,而我們這里講的是自動的測試军浆!
我們在 polls/tests.py 腳本下編寫自動化測試的代碼棕洋。
這里生成一個 TestCase 的子類。子類中定義一個 測試方法乒融,在測試方法中進(jìn)行相關(guān)的創(chuàng)建和測試操作掰盘,最終用一個 self.assertIs(方法名,期待輸出) 來決定這次測試的正確性。
最終在命令行中運(yùn)行測試: python manage.py test polls
可以看到測試過程是:
- python manage.py test polls 這個命令來尋找相應(yīng)的 test.py 中的代碼
- 它找到了 django.test.TestCase 的一個子類(即我們創(chuàng)建的子類)
- 創(chuàng)建了一個測試 database
- 在類中尋找測試方法赞季,以test開頭的方法
- 根據(jù)方法愧捕,創(chuàng)建了一個 30天后的 Question 實例
- 用 assertIs() 方法,發(fā)現(xiàn)was_published_rencently() 方法反回了 True申钩,但是我們期望返回的是 False
所以測試系統(tǒng)通知我們測試失敗了晃财。以及失敗的地方。
修復(fù)這個 bug
修復(fù)也很簡單典蜕,就是說只讓過去的時間滿足,而未來的時間都一律返回 False罗洗。
在models.py 中將原始的 was_pub_renc() 方法作相應(yīng)的更改就行愉舔。
然后重新運(yùn)行測試』锊耍可以看到測試成功了轩缤。
更全面的測試
很多時候在改正一個 bug 的時候會有另外的 bug 出現(xiàn)。所以更全面的測試是很有必要的贩绕。所以針對過去火的,現(xiàn)在,將來發(fā)布時間的實例都應(yīng)該有測試淑倾。
同樣在 polls/tests.py 中將其他兩個測試方法也寫出來馏鹤。運(yùn)行測試,發(fā)現(xiàn)通過測試娇哆。
測試視圖
其實我們的視圖也是有問題的湃累,因為對于所有的問題,都會在 index 里面 發(fā)布碍讨。但是應(yīng)該是 未來發(fā)布的問題治力,現(xiàn)在暫時不顯示。所以這個也是一個 bug勃黍。
django 測試工具 Client
這里介紹了 Client 這個測試工具宵统,其可以模擬用戶,來跟視圖層的代碼進(jìn)行交互覆获。
改善視圖代碼
修復(fù)上面說的 會顯示未來投票的問題马澈。
之前我們使用了 ListView 這個通用視圖類瓢省。這里我們需要改進(jìn)一下 get_queryset() 方法,讓她可以做一下比較來決定是否顯示這個 question 實例箭券。
這里只需要在 get_queryset() 方法中加入一個 filter 步驟净捅,
# 這里的 lte 代表 less than or equals 小于等于的意思,就是說發(fā)布時間在現(xiàn)在之前辩块。
return Question.objects.filter(publish_date__lte=timezone.now()).order_by(......)
測試新視圖
這里增加對于改進(jìn)后的新視圖的測試方法蛔六。通過上面提到過的 client() 方法進(jìn)行測試。這里給出一個例子
def test_future_question_and_past_question(self):
"""都有废亭,只顯示過去的問題"""
create_question(question_text="Future question.", days=30)
create_question(question_text="Past question.", days=-30)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(response.context['latest_question_list'], ['<Question: Past question.>'])
寫完所有的測試代碼
測試 DetailView
這里還是有一個問題国章,就算是未來的問題不會出現(xiàn),但是如果別人猜到了問題的 ID豆村,還是可以訪問液兽。所以這里還是要在 DetailView 中增加一些約束。跟 ListView 通用視圖一樣掌动,用 get_queryset() 方法來 filter四啰。
之后還可以測試一下 DetailView, 寫相關(guān)的測試類
測試越多越好
這里有幾個點(diǎn):
- 不要怕測試多粗恢,越多越好
- 每一個 model 或者 View柑晒,都應(yīng)該有獨(dú)立的 test class
- 每個測試方法都有自己獨(dú)立的測試環(huán)境,不用我們來手動的測試眷射,這就是 django 高效的原因
- 測試方法的名字要描述其功能匙赞,以 test 開始
到這里基本的測試就講完了。下面看一下靜態(tài)資源文件管理
靜態(tài)文件
服務(wù)端除了生成 HTML 之外妖碉,還需要一些額外的文件--圖片涌庭、腳本、樣式表等 來幫助渲染網(wǎng)頁欧宜。這些東西統(tǒng)稱為 靜態(tài)文件坐榆。
這里對于靜態(tài)文件存放的位置也是很有講究的。
這里我們采用 django.contrib.staticfiles 來管理各個應(yīng)用的靜態(tài)文件冗茸。
自定義應(yīng)用的界面和風(fēng)格
首先在 polls 應(yīng)用下創(chuàng)建 static 的路徑猛拴,用于存放靜態(tài)文件資源。然后在 static 下創(chuàng)立 polls 路徑蚀狰,在路徑下創(chuàng)立 style.css 的樣式文件愉昆,這跟之前創(chuàng)建 templates 一樣,需要規(guī)定 應(yīng)用名以方便區(qū)分麻蹋。
然后在 樣式表文件中寫入基本的樣式代碼跛溉,在index.html 中引入這個樣式表。重啟服務(wù)器,發(fā)現(xiàn)就會有變化芳室,連接變成了 green
添加一個背景圖
創(chuàng)建存放圖像的目錄专肪, polls/static/polls/images/background.gif
這里由于做的時候把圖片沒有放在 imgas 目錄下而是放在了其同級的地方,浪費(fèi)了一些時間堪侯。真是一個低級的錯誤嚎尤。
到這里這些基礎(chǔ)靜態(tài)資源文件的就很粗略的介紹了一下。
下面文檔介紹了如何自定義 Django 自動生成后臺網(wǎng)頁的過程伍宦。
自定義后臺表單
之前我們 通過 admin.site.register(Question) 來注冊了 Question 模型芽死。重新登錄。
注意這里說之前提到過的 admin 的登錄問題次洼,只存在于 chrome 瀏覽器中关贵,換成 IE 就沒有問題了。
這里給出了在 admin.py 中通過自定義后臺表單來更改后臺表單的樣式卖毁,如下圖揖曾。
自定義后臺表單樣式
添加關(guān)聯(lián)的對象
這里只顯示了 Question 但是沒有顯示 Choice.
這里說了一種方法,將 Choice 關(guān)聯(lián)到 Question 頁面中亥啦。就可以同時在問題頁中看到選項炭剪。
自定義后臺更改列表
這里可以通過一系列的django內(nèi)置的選項功能添加,來為 后臺表單添加各種 過濾塊翔脱,搜索框念祭,顯示方式等等。
自定義后臺界面和風(fēng)格
這里也提到了 templates 模板的 問題碍侦。文檔指出,我們將所有的模板文件放在一個地方(templates文件夾)隶糕,這里后臺的模板放在 mysite/templates瓷产,而polls之前我們用的 模板 放在 ./polls/templates 下。
大概就是這樣來自定義后臺界面枚驻。
整個Django的流程大概就是這樣濒旦。然后自己還是要從項目中來學(xué)習(xí)。