epub格式下載 感謝@Cluas
鏈接: https://pan.baidu.com/s/1kVGavLd 密碼: raxg
全文鏈接
第一章 創(chuàng)建一個(gè)blog應(yīng)用
第二章 使用高級(jí)特性來(lái)增強(qiáng)你的blog
第三章 擴(kuò)展你的blog應(yīng)用
第四章上 創(chuàng)建一個(gè)社交網(wǎng)站
第四章下 創(chuàng)建一個(gè)社交網(wǎng)站
第五章 在你的網(wǎng)站中分享內(nèi)容
第六章 跟蹤用戶動(dòng)作
第七章 建立一個(gè)在線商店
第八章 管理付款和訂單
第九章上 擴(kuò)展你的商店
第九章下 擴(kuò)展你的商店
第十章上 創(chuàng)建一個(gè)在線學(xué)習(xí)平臺(tái)
第十章下 創(chuàng)建一個(gè)在線學(xué)習(xí)平臺(tái)
第十一章 緩存內(nèi)容
第十二章 構(gòu)建一個(gè)API
書籍出處:https://www.packtpub.com/web-development/django-example
原作者:Antonio Melé
2016年12月10日發(fā)布(沒有進(jìn)行校對(duì),有很多錯(cuò)別字以及模糊不清的語(yǔ)句,請(qǐng)大家見諒)
2017年2月7日精校完成(斷斷續(xù)續(xù)的終于完成了第一章精校,感覺比直接翻譯還要累,繼續(xù)加油)
2017年2月10日再次進(jìn)行精校(感謝大牛@kukoo的精校?。?/strong>
(譯者注:本人目前在杭州某家互聯(lián)網(wǎng)公司工作,崗位是測(cè)試研發(fā),非常喜歡python,目前已經(jīng)使用Django為公司內(nèi)部搭建了幾個(gè)自動(dòng)化平臺(tái),因?yàn)闆]人教沒人帶,基本靠野路子自學(xué),走過(guò)好多彎路,磕磕碰碰一路過(guò)來(lái),前段時(shí)間偶爾看到《Django By Example》這本書,瞬間淚流滿面,當(dāng)初怎么沒有找到這么好的Django教程。在看書的過(guò)程中不知道怎么搞的突然產(chǎn)生了翻譯全書的想法,正好網(wǎng)上找了下也沒有漢化的版本,所以準(zhǔn)備踏上這條不歸路。鑒于本人英文水平極低(四級(jí)都沒過(guò)),單純靠著有道詞典和自己對(duì)上下文的理解以及對(duì)書中每行代碼都保證敲一遍并運(yùn)行的情況下,請(qǐng)各位在讀到語(yǔ)句不通的時(shí)候或看不懂的地方請(qǐng)告訴我,我會(huì)及時(shí)進(jìn)行改正。翻譯全書,主要也是為了培養(yǎng)自己的英文閱讀水平(口語(yǔ)就算了),誰(shuí)叫好多最新最有用的計(jì)算機(jī)文檔都是用英文寫的,另外也可以培養(yǎng)自己的耐心,還可以分享給其他人,就這樣。)
第一章
創(chuàng)建一個(gè)blog應(yīng)用
在這本書中,你將學(xué)習(xí)如何創(chuàng)建完整的Django項(xiàng)目,可以在生產(chǎn)環(huán)境中使用。假如你還沒有安裝Django,在本章的第一部分你將學(xué)習(xí)如何安裝。本章會(huì)覆蓋如何使用Django去創(chuàng)建一個(gè)簡(jiǎn)單的blog應(yīng)用。本章的目的是使你對(duì)該框架的工作有個(gè)基本概念,了解不同的組件之間是如何產(chǎn)生交互,并且教你一些技能通過(guò)使用一些基本功能方便地創(chuàng)建Djang項(xiàng)目。你會(huì)被引導(dǎo)創(chuàng)建一個(gè)完整的項(xiàng)目但是不會(huì)對(duì)所有的細(xì)節(jié)都進(jìn)行詳細(xì)說(shuō)明。不同的框架組件將在本書接下來(lái)的章節(jié)中進(jìn)行介紹。
本章會(huì)覆蓋以下幾點(diǎn):
- 安裝Django并創(chuàng)建你的第一個(gè)項(xiàng)目
- 設(shè)計(jì)模型(models)并且生成模型(model)數(shù)據(jù)庫(kù)遷移
- 給你的模型(models)創(chuàng)建一個(gè)管理站點(diǎn)
- 使用查詢集(QuerySet)和管理器(managers)
- 創(chuàng)建視圖(views),模板(templates)和URLs
- 給列表視圖(views)添加頁(yè)碼
- 使用Django內(nèi)置的視圖(views)
安裝Django
如果你已經(jīng)安裝好了Django,你可以直接略過(guò)這部分跳到創(chuàng)建你的第一個(gè)項(xiàng)目。Django是一個(gè)Python包因此可以安裝在任何的Python的環(huán)境中。如果你還沒有安裝Django,這里有一個(gè)快速的指南幫助你安裝Django用來(lái)本地開發(fā)。
Django需要在Python2.7或者3版本上才能更好的工作。在本書的例子中,我們將使用Python 3。如果你使用Linux或者M(jìn)ax OSX,你可能已經(jīng)有安裝好的Python。如果你不確定你的計(jì)算機(jī)中是否安裝了Python,你可以在終端中輸入 python 來(lái)確定。如果你看到以下類似的提示,說(shuō)明你的計(jì)算機(jī)中已經(jīng)安裝好了Python:
Python 3.5.0 (v3.5.0:374f501f4567, Sep 12 2015, 11:00:19)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
如果你計(jì)算機(jī)中安裝的Python版本低于3,或者沒有安裝,下載并安裝Python 3.5.0 從http://www.python.org/download/ (譯者注:最新已經(jīng)是3.6.0了,Django2.0將不再支持pytyon2.7,所以大家都從3版本以上開始學(xué)習(xí)吧)。
由于你使用的是Python3,所以你沒必要再安裝一個(gè)數(shù)據(jù)庫(kù)。這個(gè)Python版本自帶SQLite數(shù)據(jù)庫(kù)。SQLLite是一個(gè)輕量級(jí)的數(shù)據(jù)庫(kù),你可以在Django中進(jìn)行使用用來(lái)開發(fā)。如果你準(zhǔn)備在生產(chǎn)環(huán)境中部署你的應(yīng)用,你應(yīng)該使用一個(gè)更高級(jí)的數(shù)據(jù)庫(kù),比如PostgreSQL,MySQL或Oracle。你能獲取到更多的信息關(guān)于數(shù)據(jù)庫(kù)和Django的集成通過(guò)訪問(wèn) https://docs.djangoproject.com/en/1.8/topics/install/#database-installation 。
創(chuàng)建一個(gè)獨(dú)立的Python環(huán)境
強(qiáng)烈建議你使用virtualenv來(lái)創(chuàng)建獨(dú)立的Python環(huán)境,這樣你可以使用不同的包版本對(duì)應(yīng)不同的項(xiàng)目,這比直接在真實(shí)系統(tǒng)中安裝Python包更加的實(shí)用。另一個(gè)高級(jí)之處在于當(dāng)你使用virtualenv你不需要任何管理員權(quán)限來(lái)安裝Python包。在終端中運(yùn)行以下命令來(lái)安裝virtualenv:
pip install virtualenv
(譯者注:如果你本地有多個(gè)python版本,注意Python3的pip命令可能是pip3)
當(dāng)你安裝好virtualenv之后,通過(guò)以下命令來(lái)創(chuàng)建一個(gè)獨(dú)立的環(huán)境:
virtualenv my_env
以上命令會(huì)創(chuàng)建一個(gè)包含你的Python環(huán)境的my_env/目錄。當(dāng)你的virtualenv被激活的時(shí)候所有已經(jīng)安裝的Python庫(kù)都會(huì)帶入 my_env/lib/python3.5/site-packages 目錄中。
如果你的系統(tǒng)自帶Python2.X然后你又安裝了Python3.X,你必須告訴virtualenv使用后者Python3.X。通過(guò)以下命令你可以定位Python3的安裝路徑然后使用該安裝路徑來(lái)創(chuàng)建virtualenv:
zenx\$ *which python3*
/Library/Frameworks/Python.framework/Versions/3.5/bin/python3
zenx\$ *virtualenv my_env -p
/Library/Frameworks/Python.framework/Versions/3.5/bin/python3*
通過(guò)以下命令來(lái)激活你的virtualenv:
source my_env/bin/activate
shell提示將會(huì)附上激活的virtualenv名,被包含在括號(hào)中,如下所示:
(my_evn)laptop:~ zenx$
你可以使用deactivate命令隨時(shí)停用你的virtualenv。
你可以獲取更多的信息關(guān)于virtualenv通過(guò)訪問(wèn) https://virtualenv.pypa.io/en/latest/ 。
在virtualenv之上,你可以使用virtualenvwrapper工具。這個(gè)工具提供一些封裝用來(lái)方便的創(chuàng)建和管理你的虛擬環(huán)境。你可以在 http://virtualenvwrapper.readthedocs.org/en/latest/ 下載該工具。
使用pip安裝Django
(譯者注:請(qǐng)注意以下的操作都在激活的虛擬環(huán)境中使用)
pip是安裝Django的第一選擇。Python3.5自帶預(yù)安裝的pip,你可以找到pip的安裝指令通過(guò)訪問(wèn) https://pip.pypa.io/en/stable/installing/ 。運(yùn)行以下命令通過(guò)pip安裝Django:
pip install Django==1.8.6
Django將會(huì)被安裝在你的虛擬環(huán)境的Python的site-packages/目錄下。
現(xiàn)在檢查Django是否成功安裝。在終端中運(yùn)行python并且導(dǎo)入Django來(lái)檢查它的版本:
>>> import django
>>> django.VERSION
DjangoVERSION(1, 8, 5, 'final', 0)
如果你獲得了以上輸出,Django已經(jīng)成功安裝在你的機(jī)器中。
Django也可以使用其他方式來(lái)安裝。你可以找到更多的信息通過(guò)訪問(wèn) https://docs.djangoproject.com/en/1.8/topics/install/ 。
創(chuàng)建你的第一個(gè)項(xiàng)目
我們的第一個(gè)項(xiàng)目將會(huì)是一個(gè)完整的blog站點(diǎn)。Django提供了一個(gè)命令允許你方便的創(chuàng)建一個(gè)初始化的項(xiàng)目文件結(jié)構(gòu)。在終端中運(yùn)行以下命令:
django-admin startproject mysite
該命令將會(huì)創(chuàng)建一個(gè)名為mysite的項(xiàng)目。
讓我們來(lái)看下生成的項(xiàng)目結(jié)構(gòu):
mysite/
manage.py
mysite/
__init__.py
settings.py
urls.py
wsgi.py
讓我們來(lái)了解一下這些文件:
- manage.py:一個(gè)實(shí)用的命令行,用來(lái)與你的項(xiàng)目進(jìn)行交互。它是一個(gè)對(duì)django-admin.py工具的簡(jiǎn)單封裝。你不需要編輯這個(gè)文件。
-
mysite/:你的項(xiàng)目目錄,由以下的文件組成:
- init.py:一個(gè)空文件用來(lái)告訴Python這個(gè)mysite目錄是一個(gè)Python模塊。
- settings.py:你的項(xiàng)目的設(shè)置和配置。里面包含一些初始化的設(shè)置。
- urls.py:你的URL模式存放的地方。這里定義的每一個(gè)URL都映射一個(gè)視圖(view)。
- wsgi.py:配置你的項(xiàng)目運(yùn)行如同一個(gè)WSGI應(yīng)用。
默認(rèn)生成的settings.py文件包含一個(gè)使用一個(gè)SQLite數(shù)據(jù)庫(kù)的基礎(chǔ)配置以及一個(gè)Django應(yīng)用列表,這些應(yīng)用會(huì)默認(rèn)添加到你的項(xiàng)目中。我們需要為這些初始應(yīng)用在數(shù)據(jù)庫(kù)中創(chuàng)建表。
打開終端執(zhí)行以下命令:
cd mysite
python manage.py migrate
你將會(huì)看到以下的類似輸出:
Rendering model states... DONE
Applying contenttypes.ooo1_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length...OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying sessions.0001_initial... OK
這些初始應(yīng)用表將會(huì)在數(shù)據(jù)庫(kù)中創(chuàng)建。過(guò)一會(huì)兒你就會(huì)學(xué)習(xí)到一些關(guān)于migrate的管理命令。
運(yùn)行開發(fā)服務(wù)器
Django自帶一個(gè)輕量級(jí)的web服務(wù)器來(lái)快速運(yùn)行你的代碼,不需要花費(fèi)額外的時(shí)間來(lái)配置一個(gè)生產(chǎn)服務(wù)器。當(dāng)你運(yùn)行Django的開發(fā)服務(wù)器,它會(huì)一直檢查你的代碼變化。當(dāng)代碼有改變,它會(huì)自動(dòng)重啟,將你從手動(dòng)重啟中解放出來(lái)。但是,它可能無(wú)法注意到一些操作,例如在項(xiàng)目中添加了一個(gè)新文件,所以你在某些場(chǎng)景下還是需要手動(dòng)重啟。
打開終端,在你的項(xiàng)目主目錄下運(yùn)行以下代碼來(lái)開啟開發(fā)服務(wù)器:
python manage.py runserver
你會(huì)看到以下類似的輸出:
Performing system checks...
System check identified no issues (0 silenced).
November 5, 2015 - 19:10:54
Django version 1.8.6, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
現(xiàn)在,在瀏覽器中打開 http://127.0.0.1:8000/ ,你會(huì)看到一個(gè)告訴你項(xiàng)目成功運(yùn)行的頁(yè)面,如下圖所示:

你可以指定Django在定制的host和端口上運(yùn)行開發(fā)服務(wù),或者告訴它你想要運(yùn)行你的項(xiàng)目通過(guò)讀取一個(gè)不同的配置文件。例如:你可以運(yùn)行以下 manage.py命令:
python manage.py runserver 127.0.0.1:8001 \
--settings=mysite.settings
這個(gè)命令遲早會(huì)對(duì)處理需要不同設(shè)置的多套環(huán)境啟到作用。記住,這個(gè)服務(wù)器只是單純用來(lái)開發(fā),不適合在生產(chǎn)環(huán)境中使用。為了在生產(chǎn)環(huán)境中部署Django,你需要使用真實(shí)的web服務(wù)讓它運(yùn)行成一個(gè)WSGI應(yīng)用例如Apache,Gunicorn或者uWSGI(譯者注:強(qiáng)烈推薦 nginx+uwsgi+Django)。你能夠獲取到更多關(guān)于如何在不同的web服務(wù)中部署Django的信息,訪問(wèn) https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ 。
本書外額外的需要下載的章節(jié)第十三章,Going Live包含為你的Django項(xiàng)目設(shè)置一個(gè)生產(chǎn)環(huán)境。
項(xiàng)目設(shè)置
讓我們打開settings.py文件來(lái)看看你的項(xiàng)目的配置。在該文件中有許多設(shè)置是Django內(nèi)置的,但這些只是所有Django可用配置的一部分。你可以通過(guò)訪問(wèn) https://docs.djangoproject.com/en/1.8/ref/settings/ 看到所有的設(shè)置和它們默認(rèn)的值。
以下列出的設(shè)置非常值得一看:
- DEBUG 一個(gè)布爾型用來(lái)開啟或關(guān)閉項(xiàng)目的debug模式。如果設(shè)置為True,當(dāng)你的應(yīng)用拋出一個(gè)未被捕獲的異常時(shí)Django將會(huì)顯示一個(gè)詳細(xì)的錯(cuò)誤頁(yè)面。當(dāng)你準(zhǔn)備部署項(xiàng)目到生產(chǎn)環(huán)境,請(qǐng)記住一定要關(guān)閉debug模式。永遠(yuǎn)不要在生產(chǎn)環(huán)境中部署一個(gè)打開debug模式的站點(diǎn)因?yàn)槟菚?huì)暴露你的項(xiàng)目中的敏感數(shù)據(jù)。
- ALLOWED_HOSTS 當(dāng)debug模式開啟或者運(yùn)行測(cè)試的時(shí)候不會(huì)起作用(譯者注:最新的Django版本中,不管有沒有開啟debug模式該設(shè)置都會(huì)啟作用)。一旦你準(zhǔn)備部署你的項(xiàng)目到生產(chǎn)環(huán)境并且關(guān)閉了debug模式,為了允許訪問(wèn)你的Django項(xiàng)目你就必須添加你的域或host在這個(gè)設(shè)置中。
- INSTALLED_APPS 這個(gè)設(shè)置你在所有的項(xiàng)目中都需要編輯。這個(gè)設(shè)置告訴Django有哪些應(yīng)用會(huì)在這個(gè)項(xiàng)目中激活。默認(rèn)的,Django包含以下應(yīng)用:
- django.contrib.admin:這是一個(gè)管理站點(diǎn)。
- django.contrib.auth:這是一個(gè)權(quán)限框架。
- django.contrib.contenttypes:這是一個(gè)內(nèi)容類型的框架。
- django.contrib.sessions:這是一個(gè)會(huì)話(session)框架。
- django.contrib.messages:這是一個(gè)消息框架。
- django.contrib.staticfiles:這是一個(gè)用來(lái)管理靜態(tài)文件的框架
- MIDDLEWARE_CLASSES 是一個(gè)包含可執(zhí)行中間件的元組。
- ROOT_URLCONF 指明你的應(yīng)用定義的主URL模式存放在哪個(gè)Python模塊中。
- DATABASES 是一個(gè)包含了所有在項(xiàng)目中使用的數(shù)據(jù)庫(kù)的設(shè)置的字典。里面一定有一個(gè)默認(rèn)的數(shù)據(jù)庫(kù)。默認(rèn)的配置使用的是SQLite3數(shù)據(jù)庫(kù)。
- LANGUAGE_CODE 定義Django站點(diǎn)的默認(rèn)語(yǔ)言編碼。
不要擔(dān)心你目前還看不懂這些設(shè)置的含義。你將會(huì)在之后的章節(jié)中熟悉這些設(shè)置。
項(xiàng)目和應(yīng)用
貫穿全書,你會(huì)反復(fù)的讀到項(xiàng)目和應(yīng)用的地位。在Django中,一個(gè)項(xiàng)目被認(rèn)為是一個(gè)安裝了一些設(shè)置的Django;一個(gè)應(yīng)用是一個(gè)包含模型(models),視圖(views),模板(templates)以及URLs的組合。應(yīng)用之間的交互通過(guò)Django框架提供的一些特定功能,并且應(yīng)用可能被各種各樣的項(xiàng)目重復(fù)使用。你可以認(rèn)為項(xiàng)目就是你的網(wǎng)站,這個(gè)網(wǎng)站包含多個(gè)應(yīng)用,例如blog,wiki或者論壇,這些應(yīng)用都可以被其他的項(xiàng)目使用。(譯者注:我去,我竟然漏翻了這一節(jié)- -|||,罪過(guò)罪過(guò),阿米頭發(fā))
創(chuàng)建一個(gè)應(yīng)用
現(xiàn)在讓我們創(chuàng)建你的第一個(gè)Django應(yīng)用。我們將要?jiǎng)?chuàng)建一個(gè)勉強(qiáng)湊合的blog應(yīng)用。在你的項(xiàng)目主目錄下,運(yùn)行以下命令:
python manage.py startapp blog
這個(gè)命令會(huì)創(chuàng)建blog應(yīng)用的基本目錄結(jié)構(gòu),如下所示:
blog/
__init__.py
admin.py
migrations/
__init__.py
models.py
tests.py
views.py
這些文件的含義:
- admin.py: 在這兒你可以注冊(cè)你的模型(models)并將它們包含到Django的管理頁(yè)面中。使用Django的管理頁(yè)面是可選的。
- migrations: 這個(gè)目錄將會(huì)包含你的應(yīng)用的數(shù)據(jù)庫(kù)遷移。Migrations允許Django跟蹤你的模型(model)變化并因此來(lái)同步數(shù)據(jù)庫(kù)。
- models.py: 你的應(yīng)用的數(shù)據(jù)模型(models)。所有的Django應(yīng)用都需要擁有一個(gè)models.py文件,但是這個(gè)文件可以是空的。
- tests.py:在這兒你可以為你的應(yīng)用創(chuàng)建測(cè)試。
- views.py:你的應(yīng)用邏輯將會(huì)放在這兒。每一個(gè)視圖(view)都會(huì)接受一個(gè)HTTP請(qǐng)求,處理該請(qǐng)求,最后返回一個(gè)響應(yīng)。
設(shè)計(jì)blog數(shù)據(jù)架構(gòu)
我們將要開始為你的blog設(shè)計(jì)初始的數(shù)據(jù)模型(models)。一個(gè)模型(model)就是一個(gè)Python類,該類繼承了django.db.models.model,在其中的每一個(gè)屬性表示一個(gè)數(shù)據(jù)庫(kù)字段。Django將會(huì)為models.py中的每一個(gè)定義的模型(model)創(chuàng)建一張表。當(dāng)你創(chuàng)建好一個(gè)模型(model),Django會(huì)提供一個(gè)非常實(shí)用的API來(lái)方便的查詢數(shù)據(jù)庫(kù)。
首先,我們定義一個(gè)POST模型(model)。在blog應(yīng)用下的models.py文件中添加以下內(nèi)容:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Post(models.Model):
STATUS_CHOICES = (
('draft', 'Draft'),
('published', 'Published'),
)
title = models.CharField(max_length=250)
slug = models.SlugField(max_length=250,
unique_for_date='publish')
author = models.ForeignKey(User,
related_name='blog_posts')
body = models.TextField()
publish = models.DateTimeField(default=timezone.now)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=10,
choices=STATUS_CHOICES,
default='draft')
class Meta:
ordering = ('-publish',)
def __str__(self):
return self.title
這就是我們給blog帖子使用的基礎(chǔ)模型(model)。讓我們來(lái)看下剛才在這個(gè)模型(model)中定義的各個(gè)字段含義:
- title: 這個(gè)字段對(duì)應(yīng)帖子的標(biāo)題。它是CharField,在SQL數(shù)據(jù)庫(kù)中會(huì)被轉(zhuǎn)化成VARCHAR。
- slug:這個(gè)字段將會(huì)在URLs中使用。slug就是一個(gè)短標(biāo)簽,該標(biāo)簽只包含字母,數(shù)字,下劃線或連接線。我們將通過(guò)使用slug字段給我們的blog帖子構(gòu)建漂亮的,友好的URLs。我們給該字段添加了unique_for_date參數(shù),這樣我們就可以使用日期和帖子的slug來(lái)為所有帖子構(gòu)建URLs。在相同的日期中Django會(huì)阻止多篇帖子擁有相同的slug。
- author:這是一個(gè)ForeignKey。這個(gè)字段定義了一個(gè)多對(duì)一(many-to-one)的關(guān)系。我們告訴Django一篇帖子只能由一名用戶編寫,一名用戶能編寫多篇帖子。根據(jù)這個(gè)字段,Django將會(huì)在數(shù)據(jù)庫(kù)中通過(guò)有關(guān)聯(lián)的模型(model)主鍵來(lái)創(chuàng)建一個(gè)外鍵。在這個(gè)場(chǎng)景中,我們關(guān)聯(lián)上了Django權(quán)限系統(tǒng)的User模型(model)。我們通過(guò)related_name屬性指定了從User到Post的反向關(guān)系名。我們將會(huì)在之后學(xué)習(xí)到更多關(guān)于這方面的內(nèi)容。
- body:這是帖子的主體。它是TextField,在SQL數(shù)據(jù)庫(kù)中被轉(zhuǎn)化成TEXT。
- publish:這個(gè)日期表明帖子什么時(shí)間發(fā)布。我們使用Djnago的timezone的now方法來(lái)設(shè)定默認(rèn)值。This is just a timezone-aware datetime.now(譯者注:這句該咋翻譯好呢)。
- created:這個(gè)日期表明帖子什么時(shí)間創(chuàng)建。因?yàn)槲覀冊(cè)谶@兒使用了auto_now_add,當(dāng)一個(gè)對(duì)象被創(chuàng)建的時(shí)候這個(gè)字段會(huì)自動(dòng)保存當(dāng)前日期。
- updated:這個(gè)日期表明帖子什么時(shí)候更新。因?yàn)槲覀冊(cè)谶@兒使用了auto_now,當(dāng)我們更新保存一個(gè)對(duì)象的時(shí)候這個(gè)字段將會(huì)自動(dòng)更新到當(dāng)前日期。
- status:這個(gè)字段表示當(dāng)前帖子的展示狀態(tài)。我們使用了一個(gè)choices參數(shù),這樣這個(gè)字段的值只能是給予的選擇參數(shù)中的某一個(gè)值。(譯者注:傳入元組,比如
(1,2),那么該字段只能選擇1或者2,沒有其他值可以選擇)
就像你所看到的的,Django內(nèi)置了許多不同的字段類型給你使用,這樣你就能夠定義你自己的模型(models)。通過(guò)訪問(wèn) https://docs.djangoproject.com/en/1.8/ref/models/fields/ 你可以找到所有的字段類型。
在模型(model)中的類Meta包含元數(shù)據(jù)。我們告訴Django查詢數(shù)據(jù)庫(kù)的時(shí)候默認(rèn)返回的是根據(jù)publish字段進(jìn)行降序排列過(guò)的結(jié)果。我們使用負(fù)號(hào)來(lái)指定進(jìn)行降序排列。
str()方法是當(dāng)前對(duì)象默認(rèn)的可讀表現(xiàn)。Django將會(huì)在很多地方用到它例如管理站點(diǎn)中。
如果你之前使用過(guò)Python2.X,請(qǐng)注意在Python3中所有的strings都使用unicode,因此我們只使用str()方法。unicode()方法已經(jīng)廢棄。(譯者注:Python3大法好,Python2別再學(xué)了,直接學(xué)Python3吧)
在我們處理日期之前,我們需要下載pytz模塊。這個(gè)模塊給Python提供時(shí)區(qū)的定義并且SQLite也需要它來(lái)對(duì)日期進(jìn)行操作。在終端中輸入以下命令來(lái)安裝pytz:
pip install pytz
Django內(nèi)置對(duì)時(shí)區(qū)日期處理的支持。你可以在你的項(xiàng)目中的settings.py文件中通過(guò)USE_TZ來(lái)設(shè)置激活或停用對(duì)時(shí)區(qū)的支持。當(dāng)你通過(guò)startproject命令來(lái)創(chuàng)建一個(gè)新項(xiàng)目的時(shí)候這個(gè)設(shè)置默認(rèn)為True。
激活你的應(yīng)用
為了讓Django能保持跟蹤你的應(yīng)用并且根據(jù)你的應(yīng)用中的模型(models)來(lái)創(chuàng)建數(shù)據(jù)庫(kù)表,我們必須激活你的應(yīng)用。因此,編輯settings.py文件,在INSTALLED_APPS設(shè)置中添加blog??瓷先ト缦滤荆?/p>
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog',
)
(譯者注:該設(shè)置中應(yīng)用的排列順序也會(huì)對(duì)項(xiàng)目的某些方面產(chǎn)生影響,具體情況后幾章會(huì)有介紹,這里提醒下)
現(xiàn)在Django已經(jīng)知道在項(xiàng)目中的我們的應(yīng)用是激活狀態(tài)并且將會(huì)對(duì)其中的模型(models)進(jìn)行自審。
創(chuàng)建和進(jìn)行數(shù)據(jù)庫(kù)遷移
讓我們?yōu)槲覀兊哪P停╩odel)在數(shù)據(jù)庫(kù)中創(chuàng)建一張數(shù)據(jù)表格。Django自帶一個(gè)數(shù)據(jù)庫(kù)遷移(migration)系統(tǒng)來(lái)跟蹤你對(duì)模型(models)的修改,然后會(huì)同步到數(shù)據(jù)庫(kù)。migrate命令會(huì)應(yīng)用到所有在INSTALLED_APPS中的應(yīng)用,它會(huì)根據(jù)當(dāng)前的模型(models)和數(shù)據(jù)庫(kù)遷移(migrations)來(lái)同步數(shù)據(jù)庫(kù)。
首先,我們需要為我們剛才創(chuàng)建的新模型(model)創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)遷移(migration)。在你的項(xiàng)目主目錄下,執(zhí)行以下命令:
python manage.py makemigrations blog
你會(huì)看到以下輸出:
Migrations for 'blog':
0001_initial.py;
- Create model Post
Django在blog應(yīng)用下的migrations目錄中創(chuàng)建了一個(gè)0001——initial.py文件。你可以打開這個(gè)文件來(lái)看下一個(gè)數(shù)據(jù)庫(kù)遷移的內(nèi)容。
讓我們來(lái)看下Django根據(jù)我們的模型(model)將會(huì)為在數(shù)據(jù)庫(kù)中創(chuàng)建的表而執(zhí)行的SQL代碼。sqlmigrate命令帶上數(shù)據(jù)庫(kù)遷移(migration)的名字將會(huì)返回它們的SQL,但不會(huì)立即去執(zhí)行。運(yùn)行以下命令來(lái)看下輸出:
python manage.py sqlmigrate blog 0001
輸出類似如下:
BEGIN;
CREATE TABLE "blog_post" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "title" varchar(250) NOT NULL, "slug" varchar(250) NOT NULL, "body" text NOT NULL, "publish" datetime NOT NULL, "created" datetime NOT NULL, "updated" datetime NOT NULL, "status" varchar(10) NOT NULL, "author_id" integer NOT NULL REFERENCES "auth_user" ("id"));
CREATE INDEX "blog_post_2dbcba41" ON "blog_post" ("slug");
CREATE INDEX "blog_post_4f331e2f" ON "blog_post" ("author_id");
COMMIT;
Django會(huì)根據(jù)你正在使用的數(shù)據(jù)庫(kù)進(jìn)行以上精準(zhǔn)的輸出。以上SQL語(yǔ)句是為SQLite數(shù)據(jù)庫(kù)準(zhǔn)備的。如你所見,Django生成的表名前綴為應(yīng)用名之后跟上模型(model)的小寫(blog_post),但是你也可以通過(guò)在模型(models)的Meta類中使用db_table屬性來(lái)指定表名。Django會(huì)自動(dòng)為每個(gè)模型(model)創(chuàng)建一個(gè)主鍵,但是你也可以通過(guò)在模型(model)中的某個(gè)字段上設(shè)置primarry_key=True來(lái)指定主鍵。
讓我們根據(jù)新模型(model)來(lái)同步數(shù)據(jù)庫(kù)。運(yùn)行以下的命令來(lái)應(yīng)用已存在的數(shù)據(jù)遷移(migrations):
python manage.py migrate
你應(yīng)該會(huì)看到以下行跟在輸出的末尾:
Applying blog.0001_initial... OK
我們剛剛為INSTALLED_APPS中所有的應(yīng)用進(jìn)行了數(shù)據(jù)庫(kù)遷移(migrations),包括我們的blog應(yīng)用。在進(jìn)行了數(shù)據(jù)庫(kù)遷移(migrations)之后,數(shù)據(jù)庫(kù)會(huì)反映我們模型的當(dāng)前狀態(tài)。
如果為了添加,刪除,或是改變了存在的模型(models)中字段,或者你添加了新的模型(models)而編輯了models.py文件,你都需要通過(guò)使用makemigrations命令做一次新的數(shù)據(jù)庫(kù)遷移(migration)。數(shù)據(jù)庫(kù)遷移(migration)允許Django來(lái)保持對(duì)模型(model)改變的跟蹤。之后你必須通過(guò)migrate命令來(lái)保持?jǐn)?shù)據(jù)庫(kù)與我們的模型(models)同步。
為你的模型(models)創(chuàng)建一個(gè)管理站點(diǎn)
現(xiàn)在我們已經(jīng)定義好了Post模型(model),我們將要?jiǎng)?chuàng)建一個(gè)簡(jiǎn)單的管理站點(diǎn)來(lái)管理blog帖子。Django內(nèi)置了一個(gè)管理接口,該接口對(duì)編輯內(nèi)容非常的有用。這個(gè)Django管理站點(diǎn)會(huì)根據(jù)你的模型(model)元數(shù)據(jù)進(jìn)行動(dòng)態(tài)構(gòu)建并且提供一個(gè)可讀的接口來(lái)編輯內(nèi)容。你可以對(duì)這個(gè)站點(diǎn)進(jìn)行自由的定制,配置你的模型(models)在其中如何進(jìn)行顯示。
請(qǐng)記住,django.contrib.admin已經(jīng)被包含在我們項(xiàng)目的INSTALLED_APPS設(shè)置中,我們不需要再額外添加。
創(chuàng)建一個(gè)超級(jí)用戶
首先,我們需要?jiǎng)?chuàng)建一名用戶來(lái)管理這個(gè)管理站點(diǎn)。運(yùn)行以下的命令:
python manage.py createsuperuser
你會(huì)看下以下輸出。輸入你想要的用戶名,郵箱和密碼:
Username (leave blank to use 'admin'): admin
Email address: admin@admin.com
Password: ********
Password (again): ********
Superuser created successfully.
Django管理站點(diǎn)
現(xiàn)在,通過(guò)python manage.py runserver命令來(lái)啟動(dòng)開發(fā)服務(wù)器,之后在瀏覽器中打開 http://127.0.0.1:8000/admin/ 。你會(huì)看到管理站點(diǎn)的登錄頁(yè)面,如下所示:

使用你在上一步中創(chuàng)建的超級(jí)用戶信息進(jìn)行登錄。你將會(huì)看到管理站點(diǎn)的首頁(yè),如下所示:

Group和User 模型(models) 位于django.contrib.auth,是Django權(quán)限管理框架的一部分。如果你點(diǎn)擊Users,你將會(huì)看到你之前創(chuàng)建的用戶信息。你的blog應(yīng)用的Post模型(model)和User(model)關(guān)聯(lián)在了一起。記住,它們是通過(guò)author字段進(jìn)行關(guān)聯(lián)的。
在管理站點(diǎn)中添加你的模型(models)
讓我們?cè)诠芾碚军c(diǎn)中添加你的blog模型(models)。編輯blog應(yīng)用下的admin.py文件,如下所示:
from django.contrib import admin
from .models import Post
admin.site.register(Post)
現(xiàn)在,在瀏覽器中刷新管理站點(diǎn)。你會(huì)看到你的Post模型(model)已經(jīng)在頁(yè)面中展示,如下所示:

這很簡(jiǎn)單,對(duì)吧?當(dāng)你在Django的管理頁(yè)面注冊(cè)了一個(gè)模型(model),Django會(huì)通過(guò)對(duì)你的模型(models)進(jìn)行內(nèi)省然后提供給你一個(gè)非常友好有用的接口,這個(gè)接口允許你非常方便的排列,編輯,創(chuàng)建,以及刪除對(duì)象。
點(diǎn)擊Posts右側(cè)的Add鏈接來(lái)添加一篇新帖子。你將會(huì)看到Django根據(jù)你的模型(model)動(dòng)態(tài)生成了一個(gè)表單,如下所示:

Django給不同類型的字段使用了不同的表單控件。即使是復(fù)雜的字段例如DateTimeField也被展示成一個(gè)簡(jiǎn)單的接口類似一個(gè)JavaScript日期選擇器。
填寫好表單然后點(diǎn)擊Save按鈕。你會(huì)被重定向到帖子列頁(yè)面并且得到一條帖子成功創(chuàng)建的提示,如下所示:

定制models的展示形式
現(xiàn)在我們來(lái)看下如何定制管理站點(diǎn)。編輯blog應(yīng)用下的admin.py文件,使之如下所示:
from django.contrib import admin
from .models import Post
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'slug', 'author', 'publish',
'status')
admin.site.register(Post, PostAdmin)
我們使用繼承了ModelAdmin的定制類來(lái)告訴Django管理站點(diǎn)中需要注冊(cè)我們自己的模型(model)。在這個(gè)類中,我們可以包含一些關(guān)于如何在管理站點(diǎn)中展示模型(model)的信息以及如何與該模型(model)進(jìn)行交互。list_display屬性允許你在設(shè)置一些你想要在管理對(duì)象列表頁(yè)面顯示的模型(model)字段。
讓我們通過(guò)更多的選項(xiàng)來(lái)定制管理模型(model),如使用以下代碼:
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'slug', 'author', 'publish',
'status')
list_filter = ('status', 'created', 'publish', 'author')
search_fields = ('title', 'body')
prepopulated_fields = {'slug': ('title',)}
raw_id_fields = ('author',)
date_hierarchy = 'publish'
ordering = ['status', 'publish']
回到瀏覽器刷新管理站點(diǎn)頁(yè)面,現(xiàn)在應(yīng)該如下所示:

你可以看到帖子列頁(yè)面中展示的字段都是你在list-dispaly屬性中指定的。這個(gè)列頁(yè)面現(xiàn)在包含了一個(gè)右側(cè)邊欄允許你根據(jù)list_filter屬性中指定的字段來(lái)過(guò)濾返回結(jié)果。一個(gè)搜索框也應(yīng)用在頁(yè)面中。這是因?yàn)槲覀冞€通過(guò)使用search_fields屬性定義了一個(gè)搜索字段列。在搜索框的下方,有個(gè)可以通過(guò)時(shí)間層快速導(dǎo)航的欄,該欄通過(guò)定義date_hierarchy屬性出現(xiàn)。你還能看到這些帖子默認(rèn)的通過(guò)Status和Publish列進(jìn)行排序。這是因?yàn)槟阃ㄟ^(guò)使用ordering屬性指定了默認(rèn)排序。
現(xiàn)在,點(diǎn)擊Add post鏈接。你還會(huì)在這兒看到一些改變。當(dāng)你輸入完成新帖子的標(biāo)題,slug字段將會(huì)自動(dòng)填充。我們通過(guò)使用prepopulated_fields屬性告訴Django通過(guò)輸入的標(biāo)題來(lái)填充slug字段。同時(shí),現(xiàn)在的author字段展示顯示為了一個(gè)搜索控件,這樣當(dāng)你的用戶量達(dá)到成千上萬(wàn)級(jí)別的時(shí)候比再使用下拉框進(jìn)行選擇更加的人性化,如下圖所示:

通過(guò)短短的幾行代碼,我們就在管理站點(diǎn)中自定義了我們的模型(model)的展示形式。還有更多的方式可以用來(lái)定制Django的管理站點(diǎn)。在這本書的后面,我們還會(huì)進(jìn)一步講述。
使用查詢集(QuerySet)和管理器(managers)
現(xiàn)在,你已經(jīng)有了一個(gè)完整功能的管理站點(diǎn)來(lái)管理你的blog內(nèi)容,是時(shí)候?qū)W習(xí)如何從數(shù)據(jù)庫(kù)中檢索信息并且與這些信息進(jìn)行交互了。Django自帶了一個(gè)強(qiáng)大的數(shù)據(jù)庫(kù)抽象API可以讓你輕松的創(chuàng)建,檢索,更新以及刪除對(duì)象。Django的Object-relational Mapper(ORM)可以兼容MySQL,PostgreSQL,SQLite以及Oracle。請(qǐng)記住你可以在你項(xiàng)目下的setting.py中編輯DATABASES設(shè)置來(lái)指定數(shù)據(jù)庫(kù)。Django可以同時(shí)與多個(gè)數(shù)據(jù)庫(kù)進(jìn)行工作,這樣你可以編寫數(shù)據(jù)庫(kù)路由通過(guò)任何你喜歡的方式來(lái)操作數(shù)據(jù)。
一旦你創(chuàng)建好了你的數(shù)據(jù)模型(models),Django會(huì)提供你一個(gè)API來(lái)與它們進(jìn)行交互。你可以找到數(shù)據(jù)模型(model)的官方參考文檔通過(guò)訪問(wèn) https://docs.djangoproject.com/en/1.8/ref/models/ 。
創(chuàng)建對(duì)象
打開終端運(yùn)行以下命令來(lái)打開Python shell:
python manage.py shell
然后依次輸入以下內(nèi)容:
>>> from django.contrib.auth.models import User
>>> from blog.models import Post
>>> user = User.objects.get(username='admin')
>>> post = Post.objects.create(title='One more post',
slug='one-more-post',
body='Post body.',
author=user)
>>> post.save()
讓我們來(lái)研究下這些代碼做了什么。首先,我們?nèi)』亓艘粋€(gè)username是admin的用戶對(duì)象:
user = User.objects.get(username='admin')
get()方法允許你從數(shù)據(jù)庫(kù)取回一個(gè)單獨(dú)的對(duì)象。注意這個(gè)方法只希望在查詢中有唯一的一個(gè)匹配。如果在數(shù)據(jù)庫(kù)中沒有返回結(jié)果,這個(gè)方法會(huì)拋出一個(gè)DoesNotExist異常,如果數(shù)據(jù)庫(kù)返回多個(gè)匹配結(jié)果,將會(huì)拋出一個(gè)MultipleObjectsReturned異常。當(dāng)查詢執(zhí)行的時(shí)候,所有的異常都是模型(model)類的屬性。
接著,我們來(lái)創(chuàng)建一個(gè)擁有定制標(biāo)題標(biāo)題,slug和內(nèi)容的Post實(shí)例,然后我們?cè)O(shè)置之前取回的user胃這篇帖子的作者如下所示:
post = Post(title='Another post', slug='another-post', body='Postbody.', author=user)
這個(gè)對(duì)象只是存在內(nèi)存中不會(huì)執(zhí)行到數(shù)據(jù)庫(kù)中
最后,我們通過(guò)使用save()方法來(lái)保存該對(duì)象到數(shù)據(jù)庫(kù)中:
post.save()
這步操作將會(huì)執(zhí)行一段SQL的插入語(yǔ)句。我們已經(jīng)知道如何在內(nèi)存中創(chuàng)建一個(gè)對(duì)象并且之后才在數(shù)據(jù)庫(kù)中進(jìn)行插入,但是我們也可以通過(guò)使用create()方法直接在數(shù)據(jù)庫(kù)中創(chuàng)建對(duì)象,如下所示:
Post.objects.create(title='One more post', slug='one-more-post',body='Post body.', author=user)
更新對(duì)象
現(xiàn)在,改變這篇帖子的標(biāo)題并且再次保存對(duì)象:
>>> post.title = 'New title'
>>> post.save()
這一次,save()方法執(zhí)行了一條更新語(yǔ)句。
你對(duì)對(duì)象的改變一直存在內(nèi)存中直到你執(zhí)行到save()方法。
取回對(duì)象
Django的Object-relational mapping(ORM)是基于查詢集(QuerySet)。查詢集(QuerySet)是從你的數(shù)據(jù)庫(kù)中根據(jù)一些過(guò)濾條件范圍取回的結(jié)果對(duì)象進(jìn)行的采集。你已經(jīng)知道如何通過(guò)get()方法從數(shù)據(jù)庫(kù)中取回單獨(dú)的對(duì)象。如你所見:我們通過(guò)Post.objects.get()來(lái)使用這個(gè)方法。每一個(gè)Django模型(model)至少有一個(gè)管理器(manager),默認(rèn)管理器(manager)叫做objects。你通過(guò)使用你的模型(models)的管理器(manager)就能獲得一個(gè)查詢集(QuerySet)對(duì)象。獲取一張表中的所有對(duì)象,你只需要在默認(rèn)的objects管理器(manager)上使用all()方法即可,如下所示:
>>> all_posts = Post.objects.all()
這就是我們?nèi)绾蝿?chuàng)建一個(gè)用于返回?cái)?shù)據(jù)庫(kù)中所有對(duì)象的查詢集(QuerySet)。注意這個(gè)查詢集(QuerySet)并還沒有執(zhí)行。Django的查詢集(QuerySets)是惰性(lazy)的,它們只會(huì)被動(dòng)的去執(zhí)行。這樣的行為可以保證查詢集(QuerySet)非常有效率。如果我們沒有把查詢集(QuerySet)設(shè)置給一個(gè)變量,而是直接在Python shell中編寫,因?yàn)槲覀兤仁顾敵鼋Y(jié)果,這樣查詢集(QuerySet)的SQL語(yǔ)句將立馬執(zhí)行:
>>> Post.objects.all()
使用filter()方法
為了過(guò)濾查詢集(QuerySet),你可以在管理器(manager)上使用filter()方法。例如,我們可以返回所有在2015年發(fā)布的帖子,如下所示:
Post.objects.filter(publish__year=2015)
你也可以使用多個(gè)字段來(lái)進(jìn)行過(guò)濾。例如,我們可以返回2015年發(fā)布的所有作者用戶名為admin的帖子,如下所示:
Post.objects.filter(publish__year=2015, author__username='admin')
上面的寫法和下面的寫法產(chǎn)生的結(jié)果是一致的:
Post.objects.filter(publish__year=2015).filter(author__username='admin')
我們構(gòu)建了字段的查找方法,通過(guò)使用兩個(gè)下劃線
(publish__year)來(lái)查詢,除此以外我們也可以通過(guò)使用兩個(gè)下劃線(author__username)訪問(wèn)關(guān)聯(lián)的模型(model)字段。
使用exclude()
你可以在管理器(manager)上使用exclude()方法來(lái)排除某些返回結(jié)果。例如:我們可以返回所有2015年發(fā)布的帖子但是這些帖子的題目開頭不能是Why:
Post.objects.filter(publish__year=2015).exclude(title__startswith='Why')
使用order_by()
通過(guò)在管理器(manager)上使用order_by()方法來(lái)對(duì)不同的字段進(jìn)行排序,你可以對(duì)結(jié)果進(jìn)行排序。例如:你可以取回所有對(duì)象并通過(guò)它們的標(biāo)題進(jìn)行排序:
Post.objects.order_by('title')
默認(rèn)是升序。你可以通過(guò)負(fù)號(hào)來(lái)指定使用降序,如下所示:
Post.objects.order_by('-title')
刪除對(duì)象
如果你想刪除一個(gè)對(duì)象,你可以對(duì)對(duì)象實(shí)例進(jìn)行下面的操作:
post = Post.objects.get(id=1)
post.delete()
請(qǐng)注意,刪除對(duì)象也將刪除任何的依賴關(guān)系
查詢集(QuerySet)什么時(shí)候會(huì)執(zhí)行
只要你喜歡,你可以連接許多的過(guò)濾給查詢集(QuerySet)而且不會(huì)立馬在數(shù)據(jù)庫(kù)中執(zhí)行直到這個(gè)查詢集(QuerySet)被執(zhí)行。查詢集(QuerySet)只有在以下情況中才會(huì)執(zhí)行:
* 在你第一次迭代它們的時(shí)候
* 當(dāng)你對(duì)它們的實(shí)例進(jìn)行切片:例如Post.objects.all()[:3]
* 當(dāng)你對(duì)它們進(jìn)行了打包或緩存
* 當(dāng)你對(duì)它們調(diào)用了repr()或len()方法
* 當(dāng)你明確的對(duì)它們調(diào)用了list()方法
* 當(dāng)你在一個(gè)聲明中測(cè)試它,例如bool(), or, and, or if
創(chuàng)建model manager
我們之前提到過(guò), objects是每一個(gè)模型(models)的默認(rèn)管理器(manager),它會(huì)返回?cái)?shù)據(jù)庫(kù)中所有的對(duì)象。但是我們也可以為我們的模型(models)定義一些定制的管理器(manager)。我們準(zhǔn)備創(chuàng)建一個(gè)定制的管理器(manager)來(lái)返回所有狀態(tài)為已發(fā)布的帖子。
有兩種方式可以為你的模型(models)添加管理器(managers):你可以添加額外的管理器(manager)方法或者繼承管理器(manager)的查詢集(QuerySets)進(jìn)行修改。第一種方法類似Post.objects.my_manager(),第二種方法類似Post.my_manager.all()。我們的管理器(manager)將會(huì)允許我們返回所有帖子通過(guò)使用Post.published。
編輯你的blog應(yīng)用下的models.py文件添加如下代碼來(lái)創(chuàng)建一個(gè)管理器(manager):
class PublishedManager(models.Manager):
def get_queryset(self):
return super(PublishedManager,
self).get_queryset().filter(status='published')
class Post(models.Model):
# ...
objects = models.Manager() # The default manager.
published = PublishedManager() # Our custom manager.
get_queryset()是返回執(zhí)行過(guò)的查詢集(QuerySet)的方法。我們通過(guò)使用它來(lái)包含我們定制的過(guò)濾到完整的查詢集(QuerySet)中。我們定義我們定制的管理器(manager)然后添加它到Post 模型(model)中。我們現(xiàn)在可以來(lái)執(zhí)行它。例如,我們可以返回所有標(biāo)題開頭為Who的并且是已經(jīng)發(fā)布的帖子:
Post.published.filter(title__startswith='Who')
構(gòu)建列和詳情視圖(views)
現(xiàn)在你已經(jīng)學(xué)會(huì)了一些如何使用ORM的基本知識(shí),你已經(jīng)準(zhǔn)備好為blog應(yīng)用創(chuàng)建視圖(views)了。一個(gè)Django視圖(view)就是一個(gè)Python方法,它可以接收一個(gè)web請(qǐng)求然后返回一個(gè)web響應(yīng)。在視圖(views)中通過(guò)所有的邏輯處理返回期望的響應(yīng)。
首先我們會(huì)創(chuàng)建我們的應(yīng)用視圖(views),然后我們將會(huì)為每個(gè)視圖(view)定義一個(gè)URL模式,我們將會(huì)創(chuàng)建HTML模板(templates)來(lái)渲染這些視圖(views)生成的數(shù)據(jù)。每一個(gè)視圖(view)都會(huì)渲染模板(template)傳遞變量給它然后會(huì)返回一個(gè)經(jīng)過(guò)渲染輸出的HTTP響應(yīng)。
創(chuàng)建列和詳情views
讓我們開始創(chuàng)建一個(gè)視圖(view)來(lái)展示帖子列。編輯你的blog應(yīng)用下中views.py文件,如下所示:
from django.shortcuts import render, get_object_or_404
from .models import Post
def post_list(request):
posts = Post.published.all()
return render(request,
'blog/post/list.html',
{'posts': posts})
你剛創(chuàng)建了你的第一個(gè)Django視圖(view)。post_list視圖(view)將request對(duì)象作為唯一的參數(shù)。記住所有的的視圖(views)都有需要這個(gè)參數(shù)。在這個(gè)視圖(view)中,我們獲取到了所有狀態(tài)為已發(fā)布的帖子通過(guò)使用我們之前創(chuàng)建的published管理器(manager)。
最后,我們使用Django提供的快捷方法render()通過(guò)給予的模板(template)來(lái)渲染帖子列。這個(gè)函數(shù)將request對(duì)象作為參數(shù),模板(template)路徑以及變量來(lái)渲染的給予的模板(template)。它返回一個(gè)渲染文本(一般是HTML代碼)HttpResponse對(duì)象。render()方法考慮到了請(qǐng)求內(nèi)容,這樣任何模板(template)內(nèi)容處理器設(shè)置的變量都可以帶入給予的模板(template)中。你會(huì)在第三章,擴(kuò)展你的blog應(yīng)用學(xué)習(xí)到如何使用它們。
讓我們創(chuàng)建第二個(gè)視圖(view)來(lái)展示一篇單獨(dú)的帖子。添加如下代碼到views.py文件中:
def post_detail(request, year, month, day, post):
post = get_object_or_404(Post, slug=post,
status='published',
publish__year=year,
publish__month=month,
publish__day=day)
return render(request,
'blog/post/detail.html',
{'post': post})
這是一個(gè)帖子詳情視圖(view)。這個(gè)視圖(view)使用year,month,day以及post作為參數(shù)通過(guò)給予slug和日期來(lái)獲取到一篇已經(jīng)發(fā)布的帖子。請(qǐng)注意,當(dāng)我們創(chuàng)建Post模型(model)的時(shí)候,我們給slgu字段添加了unique_for_date參數(shù)。這樣我們可以確保在給予的日期中只有一個(gè)帖子會(huì)帶有一個(gè)slug,因此,我們能通過(guò)日期和slug取回單獨(dú)的帖子。在這個(gè)詳情視圖(view)中,我們通過(guò)使用get_object_or_404()快捷方法來(lái)檢索期望的Post。這個(gè)函數(shù)能取回匹配給予的參數(shù)的對(duì)象,或者當(dāng)沒有匹配的對(duì)象時(shí)返回一個(gè)HTTP 404(Not found)異常。最后,我們使用render()快捷方法來(lái)使用一個(gè)模板(template)去渲染取回的帖子。
為你的視圖(views)添加URL模式
一個(gè)URL模式是由一個(gè)Python正則表達(dá),一個(gè)視圖(view),一個(gè)全項(xiàng)目范圍內(nèi)的命名組成。Django在運(yùn)行中會(huì)遍歷所有URL模式直到第一個(gè)匹配的請(qǐng)求URL才停止。之后,Django導(dǎo)入匹配的URL模式中的視圖(view)并執(zhí)行它,使用關(guān)鍵字或指定參數(shù)來(lái)執(zhí)行一個(gè)HttpRequest類的實(shí)例。
如果你之前沒有接觸過(guò)正則表達(dá)式,你需要去稍微了解下,通過(guò)訪問(wèn) https://docs.python.org/3/howto/regex.html 。
在blog應(yīng)用目錄下創(chuàng)建一個(gè)urls.py文件,輸入以下代碼:
from django.conf.urls import url
from . import views
urlpatterns = [
# post views
url(r'^$', views.post_list, name='post_list'),
url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/(?P<post>[-\w]+)/$',
views.post_detail,
name='post_detail'),
]
第一條URL模式?jīng)]有帶入任何參數(shù),它映射到post_list視圖(view)。第二條URL模式帶上了以下4個(gè)參數(shù)映射到post_detail視圖(view)中。讓我們看下這個(gè)URL模式中的正則表達(dá)式:
- year:需要四位數(shù)
- month:需要兩位數(shù)。不及兩位數(shù),開頭帶上0,比如 01,02
- day:需要兩位數(shù)。不及兩位數(shù)開頭帶上0
- post:可以由單詞和連字符組成
為每一個(gè)應(yīng)用創(chuàng)建單獨(dú)的urls.py文件是最好的方法,可以保證你的應(yīng)用能給別的項(xiàng)目再度使用。
現(xiàn)在你需要將你blog中的URL模式包含到項(xiàng)目的主URL模式中。編輯你的項(xiàng)目中的mysite文件夾中的urls.py文件,如下所示:
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^blog/', include('blog.urls',
namespace='blog',
app_name='blog')),
]
通過(guò)這樣的方式,你告訴Django在blog/路徑下包含了blog應(yīng)用中的urls.py定義的URL模式。你可以給它們一個(gè)命名空間叫做blog,這樣你可以方便的引用這個(gè)URLs組。
模型(models)的標(biāo)準(zhǔn)URLs
你可以使用之前定義的post_detail URL給Post對(duì)象構(gòu)建標(biāo)準(zhǔn)URL。Django的慣例是給模型(model)添加get_absolute_url()方法用來(lái)返回一個(gè)對(duì)象的標(biāo)準(zhǔn)URL。在這個(gè)方法中,我們使用reverse()方法允許你通過(guò)它們的名字和可選的參數(shù)來(lái)構(gòu)建URLS。編輯你的models.py文件添加如下代碼:
from django.core.urlresolvers import reverse
Class Post(models.Model):
# ...
def get_absolute_url(self):
return reverse('blog:post_detail',
args=[self.publish.year,
self.publish.strftime('%m'),
self.publish.strftime('%d'),
self.slug])
請(qǐng)注意,我們通過(guò)使用strftime()方法來(lái)保證個(gè)位數(shù)的月份和日期需要帶上0來(lái)構(gòu)建URL(譯者注:也就是01,02,03)。我們將會(huì)在我們的模板(templates)中使用get_absolute_url()方法。
為你的視圖(views)創(chuàng)建模板(templates)
我們?yōu)槲覀兊膽?yīng)用創(chuàng)建了視圖(views)和URL模式。現(xiàn)在該添加模板(templates)來(lái)展示界面友好的帖子了。
在你的blog應(yīng)用目錄下創(chuàng)建以下目錄結(jié)構(gòu)和文件:
templates/
blog/
base.html
post/
list.html
detail.html
以上就是我們的模板(templates)的文件目錄結(jié)構(gòu)。base.html文件將會(huì)包含站點(diǎn)主要的HTML結(jié)構(gòu)以及分割內(nèi)容區(qū)域和一個(gè)導(dǎo)航欄。list.html和detail.html文件會(huì)繼承base.html文件來(lái)渲染各自的blog帖子列和詳情視圖(view)。
Django有一個(gè)強(qiáng)大的模板(templates)語(yǔ)言允許你指定數(shù)據(jù)的如何進(jìn)行展示。它基于模板標(biāo)簽(templates tags), 例如 {% tag %}, {{ variable }}以及模板過(guò)濾器(templates filters),可以對(duì)變量進(jìn)行過(guò)濾,例如 {{ variable|filter }}。你可以通過(guò)訪問(wèn) https://docs.djangoproject.com/en/1.8/ ref/templates/builtins/ 找到所有的內(nèi)置模板標(biāo)簽(templates tags)和過(guò)濾器(filters)。
讓我們來(lái)編輯base.html文件并添加如下代碼:
{% load staticfiles %}
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}{% endblock %}</title>
<link href="{% static "css/blog.css" %}" rel="stylesheet">
</head>
<body>
<div id="content">
{% block content %}
{% endblock %}
</div>
<div id="sidebar">
<h2>My blog</h2>
<p>This is my blog.</p>
</div>
</body>
</html>
{% load staticfiles %}告訴Django去加載django.contrib.staticfiles應(yīng)用提供的staticfiles 模板標(biāo)簽(temaplate tags)。通過(guò)加載它,你可以在這個(gè)模板(template)中使用{% static %}模板過(guò)濾器(template filter)。通過(guò)使用這個(gè)模板過(guò)濾器(template filter),你可以包含一些靜態(tài)文件比如說(shuō)blog.css文件,你可以在本書的范例代碼例子中找到該文件,在blog應(yīng)用的static/目錄中(譯者注:給大家個(gè)地址去拷貝 https://github.com/levelksk/django-by-example-book )拷貝這個(gè)目錄到你的項(xiàng)目下的相同路徑來(lái)使用這些靜態(tài)文件。
你可以看到有兩個(gè){% block %}標(biāo)簽(tags)。這些是用來(lái)告訴Django我們想在這個(gè)區(qū)域中定義一個(gè)區(qū)塊(block)。繼承這個(gè)模板(template)的其他模板(templates)可以使用自定義的內(nèi)容來(lái)填充區(qū)塊(block)。我們定義了一個(gè)區(qū)塊(block)叫做title,另一個(gè)區(qū)塊(block)叫做content。
讓我們編輯post/list.html文件使它如下所示:
{% extends "blog/base.html" %}
{% block title %}My Blog{% endblock %}
{% block content %}
<h1>My Blog</h1>
{% for post in posts %}
<h2>
<a href="{{ post.get_absolute_url }}">
{{ post.title }}
</a>
</h2>
<p class="date">
Published {{ post.publish }} by {{ post.author }}
</p>
{{ post.body|truncatewords:30|linebreaks }}
{% endfor %}
{% endblock %}
通過(guò){% extends %}模板標(biāo)簽(template tag),我們告訴Django需要繼承blog/base.html 模板(template)。然后我們?cè)?em>title和content區(qū)塊(blocks)中填充內(nèi)容。我們通過(guò)循環(huán)迭代帖子來(lái)展示它們的標(biāo)題,日期,作者和內(nèi)容,在標(biāo)題中還集成了帖子的標(biāo)準(zhǔn)URL鏈接。在帖子的內(nèi)容中,我們應(yīng)用了兩個(gè)模板過(guò)濾器(template filters): truncatewords用來(lái)縮短內(nèi)容限制在一定的字?jǐn)?shù)內(nèi),linebreaks用來(lái)轉(zhuǎn)換內(nèi)容中的換行符為HTML的換行符。只要你喜歡你可以連接許多模板標(biāo)簽(tempalte filters),每一個(gè)都會(huì)應(yīng)用到上個(gè)輸出生成的結(jié)果上。
打開終端執(zhí)行命令python manage.py runserver來(lái)啟動(dòng)開發(fā)服務(wù)器。在瀏覽器中打開 http://127.0.0.1:8000/blog/ 你會(huì)看到運(yùn)行結(jié)果。注意,你需要添加一些發(fā)布狀態(tài)的帖子才能在這兒看到它們。你會(huì)看到如下圖所示:

這之后,讓我們來(lái)編輯post/detail.html文件使它如下所示:
{% extends "blog/base.html" %}
{% block title %}{{ post.title }}{% endblock %}
{% block content %}
<h1>{{ post.title }}</h1>
<p class="date">
Published {{ post.publish }} by {{ post.author }}
</p>
{{ post.body|linebreaks }}
{% endblock %}
現(xiàn)在,你可以在瀏覽器中點(diǎn)擊其中一篇帖子的標(biāo)題來(lái)看帖子的詳細(xì)視圖(view)。你會(huì)看到類似以下頁(yè)面:

添加頁(yè)碼
當(dāng)你開始給你的blog添加內(nèi)容,你很快會(huì)意識(shí)到你需要將帖子分頁(yè)顯示。Django有一個(gè)內(nèi)置的Paginator類允許你方便的管理分頁(yè)。
編輯blog應(yīng)用下的views.py文件導(dǎo)入Django的頁(yè)碼類修改post_list如下所示:
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
def post_list(request):
object_list = Post.published.all()
paginator = Paginator(object_list, 3) # 3 posts in each page
page = request.GET.get('page')
try:
posts = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer deliver the first page
posts = paginator.page(1)
except EmptyPage:
# If page is out of range deliver last page of results
posts = paginator.page(paginator.num_pages)
return render(request,
'blog/post/list.html',
{'page': page,
'posts': posts})
Paginator是如何工作的:
- 我們使用希望在每頁(yè)中顯示的對(duì)象的數(shù)量來(lái)實(shí)例化Paginator類。
- 我們獲取到page GET參數(shù)來(lái)指明頁(yè)數(shù)
- 我們通過(guò)調(diào)用Paginator的 page()方法在期望的頁(yè)面中獲得了對(duì)象。
- 如果page參數(shù)不是一個(gè)整數(shù),我們就返回第一頁(yè)的結(jié)果。如果這個(gè)參數(shù)數(shù)字超出了最大的頁(yè)數(shù),我們就展示最后一頁(yè)的結(jié)果。
- 我們傳遞頁(yè)數(shù)并且獲取對(duì)象給這個(gè)模板(template)。
現(xiàn)在,我們必須創(chuàng)建一個(gè)模板(template)來(lái)展示分頁(yè)處理,它可以被任意的模板(template)包含來(lái)使用分頁(yè)。在blog應(yīng)用的templates文件夾下創(chuàng)建一個(gè)新文件命名為pagination.html。在該文件中添加如下HTML代碼:
<div class="pagination">
<span class="step-links">
{% if page.has_previous %}
<a href="?page={{ page.previous_page_number }}">Previous</a>
{% endif %}
<span class="current">
Page {{ page.number }} of {{ page.paginator.num_pages }}.
</span>
{% if page.has_next %}
<a href="?page={{ page.next_page_number }}">Next</a>
{% endif %}
</span>
</div>
為了渲染上一頁(yè)與下一頁(yè)的鏈接并且展示當(dāng)前頁(yè)面和所有頁(yè)面的結(jié)果,這個(gè)分頁(yè)模板(template)期望一個(gè)Page對(duì)象。讓我們回到blog/post/list.html模板(tempalte)中將pagination.html模板(template)包含在{% content %}區(qū)塊(block)中,如下所示:
{% block content %}
...
{% include "pagination.html" with page=posts %}
{% endblock %}
我們傳遞給模板(template)的Page對(duì)象叫做posts,我們將分頁(yè)模板(tempalte)包含在帖子列模板(template)中指定參數(shù)來(lái)對(duì)它進(jìn)行正確的渲染。這種方法你可以反復(fù)使用,用你的分頁(yè)模板(template)對(duì)不同的模型(models)視圖(views)進(jìn)行分頁(yè)處理。
現(xiàn)在,在你的瀏覽器中打開 http://127.0.0.1:8000/blog/。 你會(huì)看到帖子列的底部已經(jīng)有分頁(yè)處理:

使用基于類的視圖(views)
因?yàn)橐粋€(gè)視圖(view)的調(diào)用就是得到一個(gè)web請(qǐng)求并且返回一個(gè)web響應(yīng),你可以將你的視圖(views)定義成類方法。Django為此定義了基礎(chǔ)的視圖(view)類。它們都從View類繼承而來(lái),View類可以操控HTTP方法調(diào)度以及其他的功能。這是一個(gè)可替代的方法來(lái)創(chuàng)建你的視圖(views)。
我們準(zhǔn)備通過(guò)使用Django提供的通用ListView使我們的post_list視圖(view)轉(zhuǎn)變?yōu)橐粋€(gè)基于類的視圖。這個(gè)基礎(chǔ)視圖(view)允許你對(duì)任意的對(duì)象進(jìn)行排列。
編輯你的blog應(yīng)用下的views.py文件,如下所示:
from django.views.generic import ListView
class PostListView(ListView):
queryset = Post.published.all()
context_object_name = 'posts'
paginate_by = 3
template_name = 'blog/post/list.html'
這個(gè)基于類的的視圖(view)類似與之前的post_list視圖(view)。在這兒,我們告訴ListView做了以下操作:
- 使用一個(gè)特定的查詢集(QuerySet)代替取回所有的對(duì)象。代替定義一個(gè)queryset屬性,我們可以指定
model = Post然后Django將會(huì)構(gòu)建Post.objects.all() 查詢集(QuerySet)給我們。 - 使用環(huán)境變量posts給查詢結(jié)果。如果我們不指定任意的context_object_name默認(rèn)的變量將會(huì)是object_list。
- 對(duì)結(jié)果進(jìn)行分頁(yè)處理每頁(yè)只顯示3個(gè)對(duì)象。
- 使用定制的模板(template)來(lái)渲染頁(yè)面。如果我們不設(shè)置默認(rèn)的模板(template),ListView將會(huì)使用
blog/post_list.html。
現(xiàn)在,打開你的blog應(yīng)用下的urls.py文件,注釋到之前的post_listURL模式,在之后添加一個(gè)新的URL模式來(lái)使用PostlistView類,如下所示:
urlpatterns = [
# post views
# url(r'^$', views.post_list, name='post_list'),
url(r'^$', views.PostListView.as_view(),name='post_list'),
url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/'\
r'(?P<post>[-\w]+)/$',
views.post_detail,
name='post_detail'),
]
為了保持分頁(yè)處理能工作,我們必須將正確的頁(yè)面對(duì)象傳遞給模板(tempalte)。Django的ListView通過(guò)叫做page_obj的變量來(lái)傳遞被選擇的頁(yè)面,所以你必須編輯你的post_list.html模板(template)去包含使用了正確的變量的分頁(yè)處理,如下所示:
{% include "pagination.html" with page=page_obj %}
在你的瀏覽器中打開 http://127.0.0.1:8000/blog/ 然后檢查每一樣功能是否都和之前的post_list視圖(view)一樣工作。這是一個(gè)簡(jiǎn)單的,通過(guò)使用Django提供的通用類的基于類視圖(view)的例子。你將在第十章,創(chuàng)建一個(gè)在線學(xué)習(xí)平臺(tái)以及相關(guān)的章節(jié)中學(xué)到更多的基于類的視圖(views)。
總結(jié)
在本章中,你通過(guò)創(chuàng)建一個(gè)基礎(chǔ)的blog應(yīng)用學(xué)習(xí)了Django web框架的基礎(chǔ)。你為你的項(xiàng)目設(shè)計(jì)了數(shù)據(jù)模型(models)并且進(jìn)行了數(shù)據(jù)庫(kù)遷移。你為你的blog創(chuàng)建了視圖(views),模板(templates)以及URLs,還包括對(duì)象分頁(yè)。
在下一章中,你會(huì)學(xué)習(xí)到如何增強(qiáng)你的blog應(yīng)用,例如評(píng)論系統(tǒng),標(biāo)簽(tag)功能,并且允許你的用戶通過(guò)郵件來(lái)分享帖子。
譯者總結(jié)
終于將第一章勉強(qiáng)翻譯完成了,很多翻譯的句子我自己都讀不懂 - -|||
大家看到有錯(cuò)誤有歧義的地方請(qǐng)幫忙指出,之后還會(huì)隨時(shí)進(jìn)行修改保證基本能讀懂。
按照第一章的翻譯速度,全書都翻譯下來(lái)估計(jì)要2,3個(gè)月,這是非常非常樂(lè)觀的估計(jì),每天只有中午休息和下班后大概有兩三小時(shí)的翻譯時(shí)間。
2016年12月10日發(fā)布(沒有進(jìn)行校對(duì),有很多錯(cuò)別字以及模糊不清的語(yǔ)句,請(qǐng)大家見諒)
2017年2月7日精校完成(斷斷續(xù)續(xù)的終于完成了第一章精校,感覺比直接翻譯還要累,繼續(xù)加油)
2017年2月10日再次進(jìn)行精校(感謝大牛@kukoo的精校?。?/strong>