epub格式下載 感謝@Cluas
鏈接: https://pan.baidu.com/s/1kVGavLd 密碼: raxg
全文鏈接
第一章 創建一個blog應用
第二章 使用高級特性來增強你的blog
第三章 擴展你的blog應用
第四章上 創建一個社交網站
第四章下 創建一個社交網站
第五章 在你的網站中分享內容
第六章 跟蹤用戶動作
第七章 建立一個在線商店
第八章 管理付款和訂單
第九章上 擴展你的商店
第九章下 擴展你的商店
第十章上 創建一個在線學習平臺
第十章下 創建一個在線學習平臺
第十一章 緩存內容
第十二章 構建一個API
書籍出處:https://www.packtpub.com/web-development/django-example
原作者:Antonio Melé
2016年12月10日發布(沒有進行校對,有很多錯別字以及模糊不清的語句,請大家見諒)
2017年2月7日精校完成(斷斷續續的終于完成了第一章精校,感覺比直接翻譯還要累,繼續加油)
2017年2月10日再次進行精校(感謝大牛@kukoo的精校?。?/strong>
(譯者注:本人目前在杭州某家互聯網公司工作,崗位是測試研發,非常喜歡python,目前已經使用Django為公司內部搭建了幾個自動化平臺,因為沒人教沒人帶,基本靠野路子自學,走過好多彎路,磕磕碰碰一路過來,前段時間偶爾看到《Django By Example》這本書,瞬間淚流滿面,當初怎么沒有找到這么好的Django教程。在看書的過程中不知道怎么搞的突然產生了翻譯全書的想法,正好網上找了下也沒有漢化的版本,所以準備踏上這條不歸路。鑒于本人英文水平極低(四級都沒過),單純靠著有道詞典和自己對上下文的理解以及對書中每行代碼都保證敲一遍并運行的情況下,請各位在讀到語句不通的時候或看不懂的地方請告訴我,我會及時進行改正。翻譯全書,主要也是為了培養自己的英文閱讀水平(口語就算了),誰叫好多最新最有用的計算機文檔都是用英文寫的,另外也可以培養自己的耐心,還可以分享給其他人,就這樣。)
第一章
創建一個blog應用
在這本書中,你將學習如何創建完整的Django項目,可以在生產環境中使用。假如你還沒有安裝Django,在本章的第一部分你將學習如何安裝。本章會覆蓋如何使用Django去創建一個簡單的blog應用。本章的目的是使你對該框架的工作有個基本概念,了解不同的組件之間是如何產生交互,并且教你一些技能通過使用一些基本功能方便地創建Djang項目。你會被引導創建一個完整的項目但是不會對所有的細節都進行詳細說明。不同的框架組件將在本書接下來的章節中進行介紹。
本章會覆蓋以下幾點:
- 安裝Django并創建你的第一個項目
- 設計模型(models)并且生成模型(model)數據庫遷移
- 給你的模型(models)創建一個管理站點
- 使用查詢集(QuerySet)和管理器(managers)
- 創建視圖(views),模板(templates)和URLs
- 給列表視圖(views)添加頁碼
- 使用Django內置的視圖(views)
安裝Django
如果你已經安裝好了Django,你可以直接略過這部分跳到創建你的第一個項目。Django是一個Python包因此可以安裝在任何的Python的環境中。如果你還沒有安裝Django,這里有一個快速的指南幫助你安裝Django用來本地開發。
Django需要在Python2.7或者3版本上才能更好的工作。在本書的例子中,我們將使用Python 3。如果你使用Linux或者Max OSX,你可能已經有安裝好的Python。如果你不確定你的計算機中是否安裝了Python,你可以在終端中輸入 python 來確定。如果你看到以下類似的提示,說明你的計算機中已經安裝好了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/ (譯者注:最新已經是3.6.0了,Django2.0將不再支持pytyon2.7,所以大家都從3版本以上開始學習吧)。
由于你使用的是Python3,所以你沒必要再安裝一個數據庫。這個Python版本自帶SQLite數據庫。SQLLite是一個輕量級的數據庫,你可以在Django中進行使用用來開發。如果你準備在生產環境中部署你的應用,你應該使用一個更高級的數據庫,比如PostgreSQL,MySQL或Oracle。你能獲取到更多的信息關于數據庫和Django的集成通過訪問 https://docs.djangoproject.com/en/1.8/topics/install/#database-installation 。
創建一個獨立的Python環境
強烈建議你使用virtualenv來創建獨立的Python環境,這樣你可以使用不同的包版本對應不同的項目,這比直接在真實系統中安裝Python包更加的實用。另一個高級之處在于當你使用virtualenv你不需要任何管理員權限來安裝Python包。在終端中運行以下命令來安裝virtualenv:
pip install virtualenv
(譯者注:如果你本地有多個python版本,注意Python3的pip命令可能是pip3)
當你安裝好virtualenv之后,通過以下命令來創建一個獨立的環境:
virtualenv my_env
以上命令會創建一個包含你的Python環境的my_env/目錄。當你的virtualenv被激活的時候所有已經安裝的Python庫都會帶入 my_env/lib/python3.5/site-packages 目錄中。
如果你的系統自帶Python2.X然后你又安裝了Python3.X,你必須告訴virtualenv使用后者Python3.X。通過以下命令你可以定位Python3的安裝路徑然后使用該安裝路徑來創建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工具。這個工具提供一些封裝用來方便的創建和管理你的虛擬環境。你可以在 http://virtualenvwrapper.readthedocs.org/en/latest/ 下載該工具。
使用pip安裝Django
(譯者注:請注意以下的操作都在激活的虛擬環境中使用)
pip是安裝Django的第一選擇。Python3.5自帶預安裝的pip,你可以找到pip的安裝指令通過訪問 https://pip.pypa.io/en/stable/installing/ 。運行以下命令通過pip安裝Django:
pip install Django==1.8.6
Django將會被安裝在你的虛擬環境的Python的site-packages/目錄下。
現在檢查Django是否成功安裝。在終端中運行python并且導入Django來檢查它的版本:
>>> import django
>>> django.VERSION
DjangoVERSION(1, 8, 5, 'final', 0)
如果你獲得了以上輸出,Django已經成功安裝在你的機器中。
Django也可以使用其他方式來安裝。你可以找到更多的信息通過訪問 https://docs.djangoproject.com/en/1.8/topics/install/ 。
創建你的第一個項目
我們的第一個項目將會是一個完整的blog站點。Django提供了一個命令允許你方便的創建一個初始化的項目文件結構。在終端中運行以下命令:
django-admin startproject mysite
該命令將會創建一個名為mysite的項目。
讓我們來看下生成的項目結構:
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數據庫的基礎配置以及一個Django應用列表,這些應用會默認添加到你的項目中。我們需要為這些初始應用在數據庫中創建表。
打開終端執行以下命令:
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
這些初始應用表將會在數據庫中創建。過一會兒你就會學習到一些關于migrate的管理命令。
運行開發服務器
Django自帶一個輕量級的web服務器來快速運行你的代碼,不需要花費額外的時間來配置一個生產服務器。當你運行Django的開發服務器,它會一直檢查你的代碼變化。當代碼有改變,它會自動重啟,將你從手動重啟中解放出來。但是,它可能無法注意到一些操作,例如在項目中添加了一個新文件,所以你在某些場景下還是需要手動重啟。
打開終端,在你的項目主目錄下運行以下代碼來開啟開發服務器:
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.
現在,在瀏覽器中打開 http://127.0.0.1:8000/ ,你會看到一個告訴你項目成功運行的頁面,如下圖所示:
你可以指定Django在定制的host和端口上運行開發服務,或者告訴它你想要運行你的項目通過讀取一個不同的配置文件。例如:你可以運行以下 manage.py命令:
python manage.py runserver 127.0.0.1:8001 \
--settings=mysite.settings
這個命令遲早會對處理需要不同設置的多套環境啟到作用。記住,這個服務器只是單純用來開發,不適合在生產環境中使用。為了在生產環境中部署Django,你需要使用真實的web服務讓它運行成一個WSGI應用例如Apache,Gunicorn或者uWSGI(譯者注:強烈推薦 nginx+uwsgi+Django)。你能夠獲取到更多關于如何在不同的web服務中部署Django的信息,訪問 https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ 。
本書外額外的需要下載的章節第十三章,Going Live包含為你的Django項目設置一個生產環境。
項目設置
讓我們打開settings.py文件來看看你的項目的配置。在該文件中有許多設置是Django內置的,但這些只是所有Django可用配置的一部分。你可以通過訪問 https://docs.djangoproject.com/en/1.8/ref/settings/ 看到所有的設置和它們默認的值。
以下列出的設置非常值得一看:
- DEBUG 一個布爾型用來開啟或關閉項目的debug模式。如果設置為True,當你的應用拋出一個未被捕獲的異常時Django將會顯示一個詳細的錯誤頁面。當你準備部署項目到生產環境,請記住一定要關閉debug模式。永遠不要在生產環境中部署一個打開debug模式的站點因為那會暴露你的項目中的敏感數據。
- ALLOWED_HOSTS 當debug模式開啟或者運行測試的時候不會起作用(譯者注:最新的Django版本中,不管有沒有開啟debug模式該設置都會啟作用)。一旦你準備部署你的項目到生產環境并且關閉了debug模式,為了允許訪問你的Django項目你就必須添加你的域或host在這個設置中。
- INSTALLED_APPS 這個設置你在所有的項目中都需要編輯。這個設置告訴Django有哪些應用會在這個項目中激活。默認的,Django包含以下應用:
- django.contrib.admin:這是一個管理站點。
- django.contrib.auth:這是一個權限框架。
- django.contrib.contenttypes:這是一個內容類型的框架。
- django.contrib.sessions:這是一個會話(session)框架。
- django.contrib.messages:這是一個消息框架。
- django.contrib.staticfiles:這是一個用來管理靜態文件的框架
- MIDDLEWARE_CLASSES 是一個包含可執行中間件的元組。
- ROOT_URLCONF 指明你的應用定義的主URL模式存放在哪個Python模塊中。
- DATABASES 是一個包含了所有在項目中使用的數據庫的設置的字典。里面一定有一個默認的數據庫。默認的配置使用的是SQLite3數據庫。
- LANGUAGE_CODE 定義Django站點的默認語言編碼。
不要擔心你目前還看不懂這些設置的含義。你將會在之后的章節中熟悉這些設置。
項目和應用
貫穿全書,你會反復的讀到項目和應用的地位。在Django中,一個項目被認為是一個安裝了一些設置的Django;一個應用是一個包含模型(models),視圖(views),模板(templates)以及URLs的組合。應用之間的交互通過Django框架提供的一些特定功能,并且應用可能被各種各樣的項目重復使用。你可以認為項目就是你的網站,這個網站包含多個應用,例如blog,wiki或者論壇,這些應用都可以被其他的項目使用。(譯者注:我去,我竟然漏翻了這一節- -|||,罪過罪過,阿米頭發)
創建一個應用
現在讓我們創建你的第一個Django應用。我們將要創建一個勉強湊合的blog應用。在你的項目主目錄下,運行以下命令:
python manage.py startapp blog
這個命令會創建blog應用的基本目錄結構,如下所示:
blog/
__init__.py
admin.py
migrations/
__init__.py
models.py
tests.py
views.py
這些文件的含義:
- admin.py: 在這兒你可以注冊你的模型(models)并將它們包含到Django的管理頁面中。使用Django的管理頁面是可選的。
- migrations: 這個目錄將會包含你的應用的數據庫遷移。Migrations允許Django跟蹤你的模型(model)變化并因此來同步數據庫。
- models.py: 你的應用的數據模型(models)。所有的Django應用都需要擁有一個models.py文件,但是這個文件可以是空的。
- tests.py:在這兒你可以為你的應用創建測試。
- views.py:你的應用邏輯將會放在這兒。每一個視圖(view)都會接受一個HTTP請求,處理該請求,最后返回一個響應。
設計blog數據架構
我們將要開始為你的blog設計初始的數據模型(models)。一個模型(model)就是一個Python類,該類繼承了django.db.models.model,在其中的每一個屬性表示一個數據庫字段。Django將會為models.py中的每一個定義的模型(model)創建一張表。當你創建好一個模型(model),Django會提供一個非常實用的API來方便的查詢數據庫。
首先,我們定義一個POST模型(model)。在blog應用下的models.py文件中添加以下內容:
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數據庫中會被轉化成VARCHAR。
- slug:這個字段將會在URLs中使用。slug就是一個短標簽,該標簽只包含字母,數字,下劃線或連接線。我們將通過使用slug字段給我們的blog帖子構建漂亮的,友好的URLs。我們給該字段添加了unique_for_date參數,這樣我們就可以使用日期和帖子的slug來為所有帖子構建URLs。在相同的日期中Django會阻止多篇帖子擁有相同的slug。
- author:這是一個ForeignKey。這個字段定義了一個多對一(many-to-one)的關系。我們告訴Django一篇帖子只能由一名用戶編寫,一名用戶能編寫多篇帖子。根據這個字段,Django將會在數據庫中通過有關聯的模型(model)主鍵來創建一個外鍵。在這個場景中,我們關聯上了Django權限系統的User模型(model)。我們通過related_name屬性指定了從User到Post的反向關系名。我們將會在之后學習到更多關于這方面的內容。
- body:這是帖子的主體。它是TextField,在SQL數據庫中被轉化成TEXT。
- publish:這個日期表明帖子什么時間發布。我們使用Djnago的timezone的now方法來設定默認值。This is just a timezone-aware datetime.now(譯者注:這句該咋翻譯好呢)。
- created:這個日期表明帖子什么時間創建。因為我們在這兒使用了auto_now_add,當一個對象被創建的時候這個字段會自動保存當前日期。
- updated:這個日期表明帖子什么時候更新。因為我們在這兒使用了auto_now,當我們更新保存一個對象的時候這個字段將會自動更新到當前日期。
- status:這個字段表示當前帖子的展示狀態。我們使用了一個choices參數,這樣這個字段的值只能是給予的選擇參數中的某一個值。(譯者注:傳入元組,比如
(1,2)
,那么該字段只能選擇1或者2,沒有其他值可以選擇)
就像你所看到的的,Django內置了許多不同的字段類型給你使用,這樣你就能夠定義你自己的模型(models)。通過訪問 https://docs.djangoproject.com/en/1.8/ref/models/fields/ 你可以找到所有的字段類型。
在模型(model)中的類Meta包含元數據。我們告訴Django查詢數據庫的時候默認返回的是根據publish字段進行降序排列過的結果。我們使用負號來指定進行降序排列。
str()方法是當前對象默認的可讀表現。Django將會在很多地方用到它例如管理站點中。
如果你之前使用過Python2.X,請注意在Python3中所有的strings都使用unicode,因此我們只使用str()方法。unicode()方法已經廢棄。(譯者注:Python3大法好,Python2別再學了,直接學Python3吧)
在我們處理日期之前,我們需要下載pytz模塊。這個模塊給Python提供時區的定義并且SQLite也需要它來對日期進行操作。在終端中輸入以下命令來安裝pytz:
pip install pytz
Django內置對時區日期處理的支持。你可以在你的項目中的settings.py文件中通過USE_TZ來設置激活或停用對時區的支持。當你通過startproject命令來創建一個新項目的時候這個設置默認為True。
激活你的應用
為了讓Django能保持跟蹤你的應用并且根據你的應用中的模型(models)來創建數據庫表,我們必須激活你的應用。因此,編輯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',
)
(譯者注:該設置中應用的排列順序也會對項目的某些方面產生影響,具體情況后幾章會有介紹,這里提醒下)
現在Django已經知道在項目中的我們的應用是激活狀態并且將會對其中的模型(models)進行自審。
創建和進行數據庫遷移
讓我們為我們的模型(model)在數據庫中創建一張數據表格。Django自帶一個數據庫遷移(migration)系統來跟蹤你對模型(models)的修改,然后會同步到數據庫。migrate命令會應用到所有在INSTALLED_APPS中的應用,它會根據當前的模型(models)和數據庫遷移(migrations)來同步數據庫。
首先,我們需要為我們剛才創建的新模型(model)創建一個數據庫遷移(migration)。在你的項目主目錄下,執行以下命令:
python manage.py makemigrations blog
你會看到以下輸出:
Migrations for 'blog':
0001_initial.py;
- Create model Post
Django在blog應用下的migrations目錄中創建了一個0001——initial.py文件。你可以打開這個文件來看下一個數據庫遷移的內容。
讓我們來看下Django根據我們的模型(model)將會為在數據庫中創建的表而執行的SQL代碼。sqlmigrate命令帶上數據庫遷移(migration)的名字將會返回它們的SQL,但不會立即去執行。運行以下命令來看下輸出:
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會根據你正在使用的數據庫進行以上精準的輸出。以上SQL語句是為SQLite數據庫準備的。如你所見,Django生成的表名前綴為應用名之后跟上模型(model)的小寫(blog_post),但是你也可以通過在模型(models)的Meta類中使用db_table屬性來指定表名。Django會自動為每個模型(model)創建一個主鍵,但是你也可以通過在模型(model)中的某個字段上設置primarry_key=True來指定主鍵。
讓我們根據新模型(model)來同步數據庫。運行以下的命令來應用已存在的數據遷移(migrations):
python manage.py migrate
你應該會看到以下行跟在輸出的末尾:
Applying blog.0001_initial... OK
我們剛剛為INSTALLED_APPS中所有的應用進行了數據庫遷移(migrations),包括我們的blog應用。在進行了數據庫遷移(migrations)之后,數據庫會反映我們模型的當前狀態。
如果為了添加,刪除,或是改變了存在的模型(models)中字段,或者你添加了新的模型(models)而編輯了models.py文件,你都需要通過使用makemigrations命令做一次新的數據庫遷移(migration)。數據庫遷移(migration)允許Django來保持對模型(model)改變的跟蹤。之后你必須通過migrate命令來保持數據庫與我們的模型(models)同步。
為你的模型(models)創建一個管理站點
現在我們已經定義好了Post模型(model),我們將要創建一個簡單的管理站點來管理blog帖子。Django內置了一個管理接口,該接口對編輯內容非常的有用。這個Django管理站點會根據你的模型(model)元數據進行動態構建并且提供一個可讀的接口來編輯內容。你可以對這個站點進行自由的定制,配置你的模型(models)在其中如何進行顯示。
請記住,django.contrib.admin已經被包含在我們項目的INSTALLED_APPS設置中,我們不需要再額外添加。
創建一個超級用戶
首先,我們需要創建一名用戶來管理這個管理站點。運行以下的命令:
python manage.py createsuperuser
你會看下以下輸出。輸入你想要的用戶名,郵箱和密碼:
Username (leave blank to use 'admin'): admin
Email address: admin@admin.com
Password: ********
Password (again): ********
Superuser created successfully.
Django管理站點
現在,通過python manage.py runserver
命令來啟動開發服務器,之后在瀏覽器中打開 http://127.0.0.1:8000/admin/ 。你會看到管理站點的登錄頁面,如下所示:
使用你在上一步中創建的超級用戶信息進行登錄。你將會看到管理站點的首頁,如下所示:
Group和User 模型(models) 位于django.contrib.auth,是Django權限管理框架的一部分。如果你點擊Users,你將會看到你之前創建的用戶信息。你的blog應用的Post模型(model)和User(model)關聯在了一起。記住,它們是通過author字段進行關聯的。
在管理站點中添加你的模型(models)
讓我們在管理站點中添加你的blog模型(models)。編輯blog應用下的admin.py文件,如下所示:
from django.contrib import admin
from .models import Post
admin.site.register(Post)
現在,在瀏覽器中刷新管理站點。你會看到你的Post模型(model)已經在頁面中展示,如下所示:
這很簡單,對吧?當你在Django的管理頁面注冊了一個模型(model),Django會通過對你的模型(models)進行內省然后提供給你一個非常友好有用的接口,這個接口允許你非常方便的排列,編輯,創建,以及刪除對象。
點擊Posts右側的Add鏈接來添加一篇新帖子。你將會看到Django根據你的模型(model)動態生成了一個表單,如下所示:
Django給不同類型的字段使用了不同的表單控件。即使是復雜的字段例如DateTimeField也被展示成一個簡單的接口類似一個JavaScript日期選擇器。
填寫好表單然后點擊Save按鈕。你會被重定向到帖子列頁面并且得到一條帖子成功創建的提示,如下所示:
定制models的展示形式
現在我們來看下如何定制管理站點。編輯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']
回到瀏覽器刷新管理站點頁面,現在應該如下所示:
你可以看到帖子列頁面中展示的字段都是你在list-dispaly屬性中指定的。這個列頁面現在包含了一個右側邊欄允許你根據list_filter屬性中指定的字段來過濾返回結果。一個搜索框也應用在頁面中。這是因為我們還通過使用search_fields屬性定義了一個搜索字段列。在搜索框的下方,有個可以通過時間層快速導航的欄,該欄通過定義date_hierarchy屬性出現。你還能看到這些帖子默認的通過Status和Publish列進行排序。這是因為你通過使用ordering屬性指定了默認排序。
現在,點擊Add post鏈接。你還會在這兒看到一些改變。當你輸入完成新帖子的標題,slug字段將會自動填充。我們通過使用prepopulated_fields屬性告訴Django通過輸入的標題來填充slug字段。同時,現在的author字段展示顯示為了一個搜索控件,這樣當你的用戶量達到成千上萬級別的時候比再使用下拉框進行選擇更加的人性化,如下圖所示:
通過短短的幾行代碼,我們就在管理站點中自定義了我們的模型(model)的展示形式。還有更多的方式可以用來定制Django的管理站點。在這本書的后面,我們還會進一步講述。
使用查詢集(QuerySet)和管理器(managers)
現在,你已經有了一個完整功能的管理站點來管理你的blog內容,是時候學習如何從數據庫中檢索信息并且與這些信息進行交互了。Django自帶了一個強大的數據庫抽象API可以讓你輕松的創建,檢索,更新以及刪除對象。Django的Object-relational Mapper(ORM)可以兼容MySQL,PostgreSQL,SQLite以及Oracle。請記住你可以在你項目下的setting.py中編輯DATABASES設置來指定數據庫。Django可以同時與多個數據庫進行工作,這樣你可以編寫數據庫路由通過任何你喜歡的方式來操作數據。
一旦你創建好了你的數據模型(models),Django會提供你一個API來與它們進行交互。你可以找到數據模型(model)的官方參考文檔通過訪問 https://docs.djangoproject.com/en/1.8/ref/models/ 。
創建對象
打開終端運行以下命令來打開Python shell:
python manage.py shell
然后依次輸入以下內容:
>>> 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()
讓我們來研究下這些代碼做了什么。首先,我們取回了一個username是admin的用戶對象:
user = User.objects.get(username='admin')
get()
方法允許你從數據庫取回一個單獨的對象。注意這個方法只希望在查詢中有唯一的一個匹配。如果在數據庫中沒有返回結果,這個方法會拋出一個DoesNotExist異常,如果數據庫返回多個匹配結果,將會拋出一個MultipleObjectsReturned異常。當查詢執行的時候,所有的異常都是模型(model)類的屬性。
接著,我們來創建一個擁有定制標題標題,slug和內容的Post實例,然后我們設置之前取回的user胃這篇帖子的作者如下所示:
post = Post(title='Another post', slug='another-post', body='Postbody.', author=user)
這個對象只是存在內存中不會執行到數據庫中
最后,我們通過使用save()方法來保存該對象到數據庫中:
post.save()
這步操作將會執行一段SQL的插入語句。我們已經知道如何在內存中創建一個對象并且之后才在數據庫中進行插入,但是我們也可以通過使用create()方法直接在數據庫中創建對象,如下所示:
Post.objects.create(title='One more post', slug='one-more-post',body='Post body.', author=user)
更新對象
現在,改變這篇帖子的標題并且再次保存對象:
>>> post.title = 'New title'
>>> post.save()
這一次,save()方法執行了一條更新語句。
你對對象的改變一直存在內存中直到你執行到save()方法。
取回對象
Django的Object-relational mapping(ORM)是基于查詢集(QuerySet)。查詢集(QuerySet)是從你的數據庫中根據一些過濾條件范圍取回的結果對象進行的采集。你已經知道如何通過get()方法從數據庫中取回單獨的對象。如你所見:我們通過Post.objects.get()
來使用這個方法。每一個Django模型(model)至少有一個管理器(manager),默認管理器(manager)叫做objects。你通過使用你的模型(models)的管理器(manager)就能獲得一個查詢集(QuerySet)對象。獲取一張表中的所有對象,你只需要在默認的objects管理器(manager)上使用all()方法即可,如下所示:
>>> all_posts = Post.objects.all()
這就是我們如何創建一個用于返回數據庫中所有對象的查詢集(QuerySet)。注意這個查詢集(QuerySet)并還沒有執行。Django的查詢集(QuerySets)是惰性(lazy)的,它們只會被動的去執行。這樣的行為可以保證查詢集(QuerySet)非常有效率。如果我們沒有把查詢集(QuerySet)設置給一個變量,而是直接在Python shell中編寫,因為我們迫使它輸出結果,這樣查詢集(QuerySet)的SQL語句將立馬執行:
>>> Post.objects.all()
使用filter()方法
為了過濾查詢集(QuerySet),你可以在管理器(manager)上使用filter()
方法。例如,我們可以返回所有在2015年發布的帖子,如下所示:
Post.objects.filter(publish__year=2015)
你也可以使用多個字段來進行過濾。例如,我們可以返回2015年發布的所有作者用戶名為admin的帖子,如下所示:
Post.objects.filter(publish__year=2015, author__username='admin')
上面的寫法和下面的寫法產生的結果是一致的:
Post.objects.filter(publish__year=2015).filter(author__username='admin')
我們構建了字段的查找方法,通過使用兩個下劃線
(publish__year)
來查詢,除此以外我們也可以通過使用兩個下劃線(author__username)
訪問關聯的模型(model)字段。
使用exclude()
你可以在管理器(manager)上使用exclude()方法來排除某些返回結果。例如:我們可以返回所有2015年發布的帖子但是這些帖子的題目開頭不能是Why:
Post.objects.filter(publish__year=2015).exclude(title__startswith='Why')
使用order_by()
通過在管理器(manager)上使用order_by()方法來對不同的字段進行排序,你可以對結果進行排序。例如:你可以取回所有對象并通過它們的標題進行排序:
Post.objects.order_by('title')
默認是升序。你可以通過負號來指定使用降序,如下所示:
Post.objects.order_by('-title')
刪除對象
如果你想刪除一個對象,你可以對對象實例進行下面的操作:
post = Post.objects.get(id=1)
post.delete()
請注意,刪除對象也將刪除任何的依賴關系
查詢集(QuerySet)什么時候會執行
只要你喜歡,你可以連接許多的過濾給查詢集(QuerySet)而且不會立馬在數據庫中執行直到這個查詢集(QuerySet)被執行。查詢集(QuerySet)只有在以下情況中才會執行:
* 在你第一次迭代它們的時候
* 當你對它們的實例進行切片:例如Post.objects.all()[:3]
* 當你對它們進行了打包或緩存
* 當你對它們調用了repr()
或len()
方法
* 當你明確的對它們調用了list()
方法
* 當你在一個聲明中測試它,例如bool(), or, and, or if
創建model manager
我們之前提到過, objects是每一個模型(models)的默認管理器(manager),它會返回數據庫中所有的對象。但是我們也可以為我們的模型(models)定義一些定制的管理器(manager)。我們準備創建一個定制的管理器(manager)來返回所有狀態為已發布的帖子。
有兩種方式可以為你的模型(models)添加管理器(managers):你可以添加額外的管理器(manager)方法或者繼承管理器(manager)的查詢集(QuerySets)進行修改。第一種方法類似Post.objects.my_manager()
,第二種方法類似Post.my_manager.all()
。我們的管理器(manager)將會允許我們返回所有帖子通過使用Post.published
。
編輯你的blog應用下的models.py文件添加如下代碼來創建一個管理器(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()
是返回執行過的查詢集(QuerySet)的方法。我們通過使用它來包含我們定制的過濾到完整的查詢集(QuerySet)中。我們定義我們定制的管理器(manager)然后添加它到Post 模型(model)中。我們現在可以來執行它。例如,我們可以返回所有標題開頭為Who的并且是已經發布的帖子:
Post.published.filter(title__startswith='Who')
構建列和詳情視圖(views)
現在你已經學會了一些如何使用ORM的基本知識,你已經準備好為blog應用創建視圖(views)了。一個Django視圖(view)就是一個Python方法,它可以接收一個web請求然后返回一個web響應。在視圖(views)中通過所有的邏輯處理返回期望的響應。
首先我們會創建我們的應用視圖(views),然后我們將會為每個視圖(view)定義一個URL模式,我們將會創建HTML模板(templates)來渲染這些視圖(views)生成的數據。每一個視圖(view)都會渲染模板(template)傳遞變量給它然后會返回一個經過渲染輸出的HTTP響應。
創建列和詳情views
讓我們開始創建一個視圖(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})
你剛創建了你的第一個Django視圖(view)。post_list視圖(view)將request對象作為唯一的參數。記住所有的的視圖(views)都有需要這個參數。在這個視圖(view)中,我們獲取到了所有狀態為已發布的帖子通過使用我們之前創建的published管理器(manager)。
最后,我們使用Django提供的快捷方法render()通過給予的模板(template)來渲染帖子列。這個函數將request對象作為參數,模板(template)路徑以及變量來渲染的給予的模板(template)。它返回一個渲染文本(一般是HTML代碼)HttpResponse對象。render()方法考慮到了請求內容,這樣任何模板(template)內容處理器設置的變量都可以帶入給予的模板(template)中。你會在第三章,擴展你的blog應用學習到如何使用它們。
讓我們創建第二個視圖(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作為參數通過給予slug和日期來獲取到一篇已經發布的帖子。請注意,當我們創建Post模型(model)的時候,我們給slgu字段添加了unique_for_date參數。這樣我們可以確保在給予的日期中只有一個帖子會帶有一個slug,因此,我們能通過日期和slug取回單獨的帖子。在這個詳情視圖(view)中,我們通過使用get_object_or_404()快捷方法來檢索期望的Post。這個函數能取回匹配給予的參數的對象,或者當沒有匹配的對象時返回一個HTTP 404(Not found)異常。最后,我們使用render()快捷方法來使用一個模板(template)去渲染取回的帖子。
為你的視圖(views)添加URL模式
一個URL模式是由一個Python正則表達,一個視圖(view),一個全項目范圍內的命名組成。Django在運行中會遍歷所有URL模式直到第一個匹配的請求URL才停止。之后,Django導入匹配的URL模式中的視圖(view)并執行它,使用關鍵字或指定參數來執行一個HttpRequest類的實例。
如果你之前沒有接觸過正則表達式,你需要去稍微了解下,通過訪問 https://docs.python.org/3/howto/regex.html 。
在blog應用目錄下創建一個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模式沒有帶入任何參數,它映射到post_list視圖(view)。第二條URL模式帶上了以下4個參數映射到post_detail視圖(view)中。讓我們看下這個URL模式中的正則表達式:
- year:需要四位數
- month:需要兩位數。不及兩位數,開頭帶上0,比如 01,02
- day:需要兩位數。不及兩位數開頭帶上0
- post:可以由單詞和連字符組成
為每一個應用創建單獨的urls.py文件是最好的方法,可以保證你的應用能給別的項目再度使用。
現在你需要將你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對象構建標準URL。Django的慣例是給模型(model)添加get_absolute_url()方法用來返回一個對象的標準URL。在這個方法中,我們使用reverse()方法允許你通過它們的名字和可選的參數來構建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()方法來保證個位數的月份和日期需要帶上0來構建URL(譯者注:也就是01,02,03)。我們將會在我們的模板(templates)中使用get_absolute_url()方法。
為你的視圖(views)創建模板(templates)
我們為我們的應用創建了視圖(views)和URL模式?,F在該添加模板(templates)來展示界面友好的帖子了。
在你的blog應用目錄下創建以下目錄結構和文件:
templates/
blog/
base.html
post/
list.html
detail.html
以上就是我們的模板(templates)的文件目錄結構。base.html文件將會包含站點主要的HTML結構以及分割內容區域和一個導航欄。list.html和detail.html文件會繼承base.html文件來渲染各自的blog帖子列和詳情視圖(view)。
Django有一個強大的模板(templates)語言允許你指定數據的如何進行展示。它基于模板標簽(templates tags), 例如 {% tag %}
, {{ variable }}
以及模板過濾器(templates filters),可以對變量進行過濾,例如 {{ variable|filter }}
。你可以通過訪問 https://docs.djangoproject.com/en/1.8/ ref/templates/builtins/ 找到所有的內置模板標簽(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),你可以包含一些靜態文件比如說blog.css文件,你可以在本書的范例代碼例子中找到該文件,在blog應用的static/目錄中(譯者注:給大家個地址去拷貝 https://github.com/levelksk/django-by-example-book )拷貝這個目錄到你的項目下的相同路徑來使用這些靜態文件。
你可以看到有兩個{% block %}
標簽(tags)。這些是用來告訴Django我們想在這個區域中定義一個區塊(block)。繼承這個模板(template)的其他模板(templates)可以使用自定義的內容來填充區塊(block)。我們定義了一個區塊(block)叫做title,另一個區塊(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區塊(blocks)中填充內容。我們通過循環迭代帖子來展示它們的標題,日期,作者和內容,在標題中還集成了帖子的標準URL鏈接。在帖子的內容中,我們應用了兩個模板過濾器(template filters): truncatewords用來縮短內容限制在一定的字數內,linebreaks用來轉換內容中的換行符為HTML的換行符。只要你喜歡你可以連接許多模板標簽(tempalte filters),每一個都會應用到上個輸出生成的結果上。
打開終端執行命令python manage.py runserver
來啟動開發服務器。在瀏覽器中打開 http://127.0.0.1:8000/blog/ 你會看到運行結果。注意,你需要添加一些發布狀態的帖子才能在這兒看到它們。你會看到如下圖所示:
這之后,讓我們來編輯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 %}
現在,你可以在瀏覽器中點擊其中一篇帖子的標題來看帖子的詳細視圖(view)。你會看到類似以下頁面:
添加頁碼
當你開始給你的blog添加內容,你很快會意識到你需要將帖子分頁顯示。Django有一個內置的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是如何工作的:
- 我們使用希望在每頁中顯示的對象的數量來實例化Paginator類。
- 我們獲取到page GET參數來指明頁數
- 我們通過調用Paginator的 page()方法在期望的頁面中獲得了對象。
- 如果page參數不是一個整數,我們就返回第一頁的結果。如果這個參數數字超出了最大的頁數,我們就展示最后一頁的結果。
- 我們傳遞頁數并且獲取對象給這個模板(template)。
現在,我們必須創建一個模板(template)來展示分頁處理,它可以被任意的模板(template)包含來使用分頁。在blog應用的templates文件夾下創建一個新文件命名為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>
為了渲染上一頁與下一頁的鏈接并且展示當前頁面和所有頁面的結果,這個分頁模板(template)期望一個Page對象。讓我們回到blog/post/list.html模板(tempalte)中將pagination.html模板(template)包含在{% content %}
區塊(block)中,如下所示:
{% block content %}
...
{% include "pagination.html" with page=posts %}
{% endblock %}
我們傳遞給模板(template)的Page對象叫做posts,我們將分頁模板(tempalte)包含在帖子列模板(template)中指定參數來對它進行正確的渲染。這種方法你可以反復使用,用你的分頁模板(template)對不同的模型(models)視圖(views)進行分頁處理。
現在,在你的瀏覽器中打開 http://127.0.0.1:8000/blog/。 你會看到帖子列的底部已經有分頁處理:
使用基于類的視圖(views)
因為一個視圖(view)的調用就是得到一個web請求并且返回一個web響應,你可以將你的視圖(views)定義成類方法。Django為此定義了基礎的視圖(view)類。它們都從View類繼承而來,View類可以操控HTTP方法調度以及其他的功能。這是一個可替代的方法來創建你的視圖(views)。
我們準備通過使用Django提供的通用ListView使我們的post_list視圖(view)轉變為一個基于類的視圖。這個基礎視圖(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將會構建Post.objects.all() 查詢集(QuerySet)給我們。 - 使用環境變量posts給查詢結果。如果我們不指定任意的context_object_name默認的變量將會是object_list。
- 對結果進行分頁處理每頁只顯示3個對象。
- 使用定制的模板(template)來渲染頁面。如果我們不設置默認的模板(template),ListView將會使用
blog/post_list.html
。
現在,打開你的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)的例子。你將在第十章,創建一個在線學習平臺以及相關的章節中學到更多的基于類的視圖(views)。
總結
在本章中,你通過創建一個基礎的blog應用學習了Django web框架的基礎。你為你的項目設計了數據模型(models)并且進行了數據庫遷移。你為你的blog創建了視圖(views),模板(templates)以及URLs,還包括對象分頁。
在下一章中,你會學習到如何增強你的blog應用,例如評論系統,標簽(tag)功能,并且允許你的用戶通過郵件來分享帖子。
譯者總結
終于將第一章勉強翻譯完成了,很多翻譯的句子我自己都讀不懂 - -|||
大家看到有錯誤有歧義的地方請幫忙指出,之后還會隨時進行修改保證基本能讀懂。
按照第一章的翻譯速度,全書都翻譯下來估計要2,3個月,這是非常非常樂觀的估計,每天只有中午休息和下班后大概有兩三小時的翻譯時間。
2016年12月10日發布(沒有進行校對,有很多錯別字以及模糊不清的語句,請大家見諒)
2017年2月7日精校完成(斷斷續續的終于完成了第一章精校,感覺比直接翻譯還要累,繼續加油)
2017年2月10日再次進行精校(感謝大牛@kukoo的精校!)