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

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

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

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

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

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

定制models的展示形式
現(xiàn)在我們來看下如何定制管理站點。編輯blog應用下的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的定制類來告訴Django管理站點中需要注冊我們自己的模型(model)。在這個類中,我們可以包含一些關于如何在管理站點中展示模型(model)的信息以及如何與該模型(model)進行交互。list_display屬性允許你在設置一些你想要在管理對象列表頁面顯示的模型(model)字段。
讓我們通過更多的選項來定制管理模型(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']
回到瀏覽器刷新管理站點頁面,現(xiàn)在應該如下所示:

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

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

添加頁碼
當你開始給你的blog添加內(nèi)容,你很快會意識到你需要將帖子分頁顯示。Django有一個內(nèi)置的Paginator類允許你方便的管理分頁。
編輯blog應用下的views.py文件導入Django的頁碼類修改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是如何工作的:
- 我們使用希望在每頁中顯示的對象的數(shù)量來實例化Paginator類。
- 我們獲取到page GET參數(shù)來指明頁數(shù)
- 我們通過調(diào)用Paginator的 page()方法在期望的頁面中獲得了對象。
- 如果page參數(shù)不是一個整數(shù),我們就返回第一頁的結(jié)果。如果這個參數(shù)數(shù)字超出了最大的頁數(shù),我們就展示最后一頁的結(jié)果。
- 我們傳遞頁數(shù)并且獲取對象給這個模板(template)。
現(xiàn)在,我們必須創(chuàng)建一個模板(template)來展示分頁處理,它可以被任意的模板(template)包含來使用分頁。在blog應用的templates文件夾下創(chuàng)建一個新文件命名為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>
為了渲染上一頁與下一頁的鏈接并且展示當前頁面和所有頁面的結(jié)果,這個分頁模板(template)期望一個Page對象。讓我們回到blog/post/list.html模板(tempalte)中將pagination.html模板(template)包含在{% content %}區(qū)塊(block)中,如下所示:
{% block content %}
...
{% include "pagination.html" with page=posts %}
{% endblock %}
我們傳遞給模板(template)的Page對象叫做posts,我們將分頁模板(tempalte)包含在帖子列模板(template)中指定參數(shù)來對它進行正確的渲染。這種方法你可以反復使用,用你的分頁模板(template)對不同的模型(models)視圖(views)進行分頁處理。
現(xiàn)在,在你的瀏覽器中打開 http://127.0.0.1:8000/blog/。 你會看到帖子列的底部已經(jīng)有分頁處理:

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