以正確的方式開始一個 Django 1.6 專案

類別: IT
標籤: django

早在2012年二月份時,我寫了一篇題為”以正確的方式開始一個Django專案的文章,緊隨的是“以正確的方式開始一個 Django 1.4 專案”(OSC翻譯地址)。這兩篇文章得到了連續的關注,並在StackOverflow回答、維基參考以及Tweets上被援引了多次。現在Django1.5和1.6已經出來了,似乎是應該更新文章了。

專案開始時是一個關鍵時刻,選擇會對專案產生長期的影響。有很多關於如何開始使用Django框架的教程,但很少討論如何專業地使用Django,或如何使用行業公認的最佳做法來確保你的專案規模的持續增長。事前的籌劃讓你(和所有同事的生活)在走向將來時更容易。

文章結束時,你將有

  1. 一個全功能的Django 1.6專案

  2. 原始碼受控的所有資源(使用Git或Mercurial)

  3. 自動迴歸和單元測試(使用unittest庫)

  4. 一個獨立於特定環境的安裝專案(使用virtualenv)

  5. 自動化的部署和測試(使用Fabric)

  6. 自動資料庫遷移 (使用South)

  7. 一個標度你站點的開發工作流程

除第一部在官方教程中外其他部分教程裡都沒有。它們應該這樣。如果你想開始一個新的、生產就緒的Django 1.6專案,請繼續往下看。

先決條件

假定你已瞭解Python的基本知識,同時,以往的一些Django經驗會有幫助,但這不是必要的。你需要gitMercurial來進行版本控制。就這些!

準備安裝

我假設你已經安裝了Python。如果你沒有的話到python.org找到與你係統架構相符的版本下載安裝。我使用一個Linode上的64位的Ubuntu伺服器,我很高興使用Linode的服務。

那麼,第一步是什麼呢?安裝Django?不完全是。將安裝包直接安裝到你當前的site-packages裡有一個常見的問題:如果你的機器上有一個以上的Python專案使用Django等其他庫,你可能會碰到應用和安裝軟體庫之間依賴性的問題。因此,我們將使用virtualenv和它的延展virtualenvwrapper來管理我們的Django安裝。這是Python和Django使用者的實踐建議。

如果你使用pip來安裝第三方庫(我不明白你為什麼不),你可以通過簡單的操作安裝virtualenv和virtualenvwrapper。

$ pip install virtualenvwrapper

安裝完後,將下附內容新增到你的shell啟動配置檔案中(.zshrc、.bashrc、.profile等)

export WORKON_HOME=$HOME/.virtualenvsexport PROJECT_HOME=$HOME/directory-you-do-development-insource /usr/local/bin/virtualenvwrapper.sh

過載一下你的啟動配置檔案(source .zshrc),現在你已經就緒了。

建立一個新環境

建立一個虛擬環境很簡單,只需輸入

$ mkvirtualenv django_project

“django_project”是你的專案的命名。

你會注意到立馬發生的一些事情:

  • 你的shell前面加上了“(django_project)”

  • distribute和pip被自動安裝了

這裡是virtualenvwrapper的一個很有用的部分:它會自動為你準備好環境,讓你馬上可以使用pip安裝庫。“(django_project)”的部分是提醒你正在使用的是virtualenv而不是你係統上的Python。要退出虛擬環境只需簡單輸入deactivate即可。當你要回到你的專案開始工作時,只需使用workon django_project即可。需要注意的是這與vanilla virtualenv工具不同,在哪裡執行這些命令都可以。

安裝Django

“等一下,‘安裝Django’?我已經安裝Django了!”,太好了。不過你不會用它的。相反,我們將使用機器上的一個被virtualenv管理的且不會被其他使用者(或你自己)弄亂的Django安裝。在virtualenv中安裝Django,只需輸入:

$ pip install django

這樣最新版的Django將被安裝在你的virtualenv環境裡,你可以這樣確認:

$ which django-admin.py

這會指出你的$HOME/.virtualenvs/目錄。如果沒有的話,確認你的輸入提示裡有“(django_project)”。如果沒有,使用workon django_project啟用virtualenv。

建立專案

在我們真正開始這個專案之前,我們先來談一談。我在過去的幾年裡諮詢過很多Django/Python專案並且和許多開發者討論過。一個具有壓倒性的事實是,那些具有最多困難的事情往往都沒有使用任何的版本控制。這聽起來讓人難以置信(想想GitHub的流行程度吧),但是開發者們根本不會去接觸版本控制。也有一些人認為"這是個小專案",沒有必要使用版本控制。這是錯誤的。

這裡列出的工具不會讓你為了使用版本控制而增加更多額外的支出。

之前,我只提到過git作為(D)VCS。但是,既然這個專案是Python寫的,Mercurial也是一個基於Python的不錯的選擇。因為兩者都比較流行,所以你能找到許多線上學習資源。確保你已經安裝了git或者Mercurial。兩者都可以通過你的distro's packaging系統獲取它們。

如果你打算用git,GitHub顯然是一個很好的選擇可以把你的程式碼儲存到遠端倉庫裡。使用Mercurial的話, Atlassian的Bitbucket是一個不錯的選擇(它也支援git,所以你用git或者Mercurial都行)

(原始碼)控制你的環境

即使我們還沒真正做什麼,但我們知道我們想讓所有東西都在原始碼控制下。我們有兩類“東西”將提交:你的程式碼本身(包括模板,等等)和支援檔案,像資料庫夾具、South遷移(以後會更多)和requirements.txt檔案,列出你的專案依賴的所有包,允許自動構建環境(不需要再次使用pip install安裝所有包)。

讓我們開始建立我們的專案資料夾。使用django-admin.py提供的startproject命令來設定。

$ django-admin.py startproject django_project

我們將看到建立了一個單獨的資料夾:django_project。在django_project資料夾內,我們將看到 另一個包含了常見元素的django_project資料夾:setting.py,urls.py和wsgi.py。在第二個django_project資料夾的同一級內有manage.py檔案。

插曲:專案vs.應用程式

你也許會好奇,在Django1.4中,為什麼已經有了新建應用程式的命令還要增加新建專案的命令。答案在於Django“專案”和Django“應用程式”的區別。簡單來說,一個專案是一套完整的網站或者應用。一個“應用程式”是一個可以用在任何Django專案中的很小的、(希望是)獨立的Django應用。如果你正在構建一個叫做“超級部落格”的部落格應用,那麼“超級部落格”就是你的Django專案。如果“超級部落格”支援讀者投票,那“投票”就是被“超級部落格“使用的一個Django應用程式。這個概念就是需要你的投票應用程式可以應用在其他需要使用者投票的Django專案中,而不是僅能應用在”超級部落格“專案中。一個專案就是一堆應用程式按照專案特定的邏輯構建的一個集合。一個應用程式可以應用在多個專案中。

儘管你會本能地傾向於在你的“投票”應用程式中包含大量"超級部落格"的特定程式碼和資訊,但避免這樣有許多好處。基於鬆耦合的原理,將你的應用編寫為一個獨立的實體可以保持設計意圖,並且可以避免專案裡的bug直接影響到你的應用。這也意味著,如果你希望的話,你可以把你的任何應用程式發給另一個開發者,且他們不需要訪問或更改你的主專案。

像軟體開發中的許多事情一樣,這需要一點付出,但回報很豐厚。

建立倉庫

現在我們的專案裡已經有一些“程式碼”了(確實來說只是一些股票指令碼和空的配置檔案,恕我這樣說),現在是我們初始我們原始碼控制庫再好不過的時間了。下面是在Git和Mercurial中實現的步驟。

git

$ git init

這條命令在當前目錄建立了一個git倉庫。將我們所有的檔案新增到git以便提交。

$ git add django_project

現在,我們將程式碼切實提交到我們的新庫中:

$ git commit -m 'Initial commit of django_project'

Mercurial

$ hg init

這條命令在當前目錄建立了一個Mercurial倉庫。將我們所有的檔案新增到git以便提交。

$ hg add django_project

現在,我們將程式碼切實提交到我們的新庫中:

$ hg commit -m 'Initial commit of django_project'

如果你打算使用像GitHub或者Bitbucket,現在是時候把程式碼push上去了。

使用South進行資料庫遷移

Django中最令人沮喪的特性之一是管理模型的變化和資料庫的相關變化。有了South的幫助,你可以實現建立一個完整的應用,而不需要寫具體的資料庫程式碼。South會建立一個遷移檔案來檢測你的模型變化,並自動在資料庫中生成。這使得你既可以前向根據最新變化來遷移資料庫,又可以後向取消一個變化或者一系列變化。它讓你的生活如此簡單,Django發行版沒有包含它真讓人吃驚。

何時開始使用South

在前面的文章中,我建議在專案的一開始就使用South。對於相對簡單的專案而已,這挺好。然而,如果在你的原型中有大量的模型有很大的變化,那現在不是使用South的時候。對應的,僅僅丟掉並在需要時重建資料庫。你可以編寫指令碼來構成擁有一些測試資料的資料庫,並在需要時編輯它們。然而,一旦你的模型不再變化,儘快開始使用South。這很簡單:

./manage.py convert_to_south <app_name>

安裝和設定

仍然在我們的虛擬環境下,像這樣安裝South:

$ pip install south

我們在專案的settings.py檔案中把South新增到INSTALLED_APS中。現在就新增,包括你的專案中的資料庫設定,然後執行python manage.py syncdb。你將需要提升許可權使用超級使用者名稱和密碼(你可以輸入然後回車)。更重要的是,South已經在資料庫中設定好了它需要用的表格。

你可能認識到我們並沒有在專案中新增應用,而只是執行了 syncdb。先這樣做可以讓 South 在一開始的時候就被安裝。使用 South,在我們應用中的所有遷移工作都可以完成,包括初始遷移

由於我們剛剛完成了很多變更,現在將是一個提交的好時間。你得適應頻繁的提交,要知道,提交的粒度越小,在出錯時回退的自由度越高。

要進行提交操作,讓我們看看都有那些修改。

(git)

$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
##       modified:   django_project/settings.py
## Untracked files:
#   (use "git add <file>..." to include in what will be committed)
##       django_project/.settings.py.swp
#       django_project/__init__.pyc
#       django_project/settings.pyc

(Mercurial)

$ hg status
M django_project/django_project/settings.py
? django_project/django_project/.settings.py.swp
? django_project/django_project/__init__.pyc
? django_project/django_project/settings.pyc

使用 git 和 Mercurial,你可能發現一些你永遠都不希望提交的檔案,例如上面出現的   python 編譯 後的 .pyc 檔案,以及 vim 的.swp 交換檔案。要忽略這些檔案,在專案的根目錄中建立一個 .gitignore 或 .hgignore 檔案,並在其中新增匹配你希望追蹤的檔案的 shell 模式。例如,我的檔案內容多半就是:

*.pyc.*swp

在我們提交之前,還有一個資訊需要檢視:我們已經安裝的 Python 包。我們希望能夠追蹤使用到的 Python 包的名稱版本,這樣一來,我們就可以輕鬆的重建生產環境。pip 正好有個命令可以完成我們這個需求。

$ pip freeze > requirements.txt

我將 pip 的輸出存入名為 requirements.txt 的檔案,並將這個檔案新增到程式碼控制中。這樣,我們總是擁有一個更新的列表,裡面包含了將使用的包。

現在將 settings.py 及 requirements.txt 新增到提交檔案中,並提交:

$ (git/hg) add django_project/settings.py requirements.txt
$ (git/hg) commit -m 'Added South for database migrations'

新型設定

隨著開發者對Django和Python越來越舒適,他們會發覺settings.py就是個簡單的Python指令碼,因此可以“編寫”。對settings.py的一個常見方式是從一個頗為古怪的專案資料夾移動到一個新的叫做conf或者config的資料夾。要清楚你需要對manage.py做些小改變來適應這個移動。

在setting.py內,INSTALLED_APPS會很快變成一堆第三方的包,自身django應用和專案特定的應用。我習慣把INSTALLED_APPS分成三個類別:

  • DEFAULT_APPS:作為預設Django安裝(像admin)的一部分的Django框架應用

  • THIRD_PARTY_APPS:像South

  • LOCAL_APPS:你建立的應用

這可以更容易的看出哪些是你使用的第三方應用,哪些是專案自身的。僅僅記住最後有一行和下面相似的程式碼:

INSTALLED_APPS = DEFAULT_APPS + THIRD_PARTY_APPS + LOCAL_APPS

否則,Django將會出現沒有定義INSTALLED_APPS的錯誤。

建立我們的應用

以正常的方式使用manage.py來建立一個應用(python manage.py startapp myapp),並把它加入到INSTALLED_APP中。同時,花費時間讓manage.py可執行(chmod +x manage.py),這樣你就可以僅僅輸入./manage.py <command>,而不需要總是輸入python manage.py <command>。老實說,很少有開發者這麼做。我無法搞清楚為什麼。

在新增模型前,我們要做的第一件事是我們告訴South我們想用它做遷移:

$ python manage.py schemamigration myapp --initial

這將建立一個移植檔案,用來應用我們對於資料庫的模型更改(如果我們有的話),而不需要完全銷燬再重建它。當情況偏離時,它也可以讓我們用來 恢復更改。我們使用移植檔案來 移植資料庫的變化(即使還沒有變化),命令如下:

$ python manage.py migrate myapp

South足夠智慧,知道去哪裡找到移植檔案,也記得我們做的最後的移植。你可以指定單獨的移植檔案,但這一般並不是必須的。

當我們最終對模型做出改變時,我們使用下面的命令來讓South建立一個移植檔案:

$ python manage.py schemamigration myapp --auto

這將檢測myapp中的模型,並自動相應的新增、刪除或修改資料庫中的表。然後使用如上的移植命令就可以將改變應用到資料庫上。

開發區域

還有一件事你需要注意:將開發區域與你已經確認的檔案區分開,原因顯而易見。使用Git和Mercurial實現這個很簡單,而且也有助於部署。建立django_project所在目錄之外的一個目錄作為你開發區域(我把它叫做dev)。

在你的開發目錄,使用git或Mercurial克隆當前專案:

$ (git/hg) clone /path/to/my/project/

兩個工具都將建立庫的一份完整拷貝。所有的更改、分支及歷史都將在新庫中可用。從現在起,你應該在你的開發目錄工作。

由於使用Git和Mercurial來進行分支都容易便捷,當你切換新分支或站點的大規模變化時建立分支。下面是兩個工具的實現方法:

(git)

$ git checkout -b <branchname>

這不僅建立了一個命名新分支且會將程式碼檢出。幾乎所有的開發工作都應該在分支上,這樣主分支可以隨時恢復。

(Mercurial)

$ hg branch <branchname>

請注意,在Mercurial社群裡分支是一個有爭議的話題,目前這裡有一些可用的選項,但其中還沒有“顯然正確”的。在這裡,我使用命名分支,這可能是最安全且最有益的分支風格。任何在branch命令後的提交將在新分支生效。

使用 Fabric 來進行部署

那麼我們就有了一個Django應用。我們怎麼來部署它呢?Fabric。對一個合理大小的專案來說,討論任何其它的東西都是浪費時間。Fabric可用於許多種不同目的,不過在部署方面它確實很出色。

$ pip install fabric

Fabric 需要一個名為fabfile.py的 fabfile 檔案,這個檔案定義了所有我們可以採用的動作。現在我們就來建立它。將下面這些內容寫入fabfile.py並將其放到專案的根目錄。

from fabric.api import localdef prepare_deployment(branch_name):
    local('python manage.py test django_project')
    local('git add -p && git commit') # or local('hg add && hg commit')

這樣就會執行這個測試並提交你的變更,但是提交只在測試通過的條件下發生。在此處,生產環境中一個簡單的"pull"動作都可以成為實際部署。我們給實際部署再增加一些東西。將以下內容增加到fabfile.py:

from fabric.api import lcd, localdef deploy():
    with lcd('/path/to/my/prod/area/'):

        # With git...
        local('git pull /my/path/to/dev/area/')

        # With Mercurial...
        local('hg pull /my/path/to/dev/area/')
        local('hg update')

        # With both
        local('python manage.py migrate myapp')
        local('python manage.py test myapp')
        local('/my/command/to/restart/webserver')

這將會從開發主分支拉回(pull)變更,執行你實施的任何遷移,執行測試,並且重啟你的web伺服器。這些只需在命令列中的一條簡單的命令。如果其中的一條命令失敗了,指令碼將會停止執行並報告發生的事情。一旦你修復了這個問題,無需再手工執行其餘步驟。因為它們是冪等的,你只需重新執行部署命令,一切都將恢復正常。

(譯註:idempotent 冪等,某一元運算為冪等的時,其作用在任一元素兩次後會和其作用一次的結果相同。)

注意上面的程式碼是假設你部署在相同的機器上。如果不是這樣的話,這個檔案很可能相同,但是會使用Fabric的run函式來替代local。參見Fabric 文件 獲取更多細節。

現在我們建立了fabfile.py,該怎樣實際部署呢?很簡單。只需執行:

$ fab prepare_deployment$ fab deploy

在技術層面,這些可以合併為一個單獨的命令,但是我覺得最好明確的準備你的部署工作再部署它,因為這樣就使你更關注於你正在做的事情。

建立單元測試

如果你但凡聽說過我,可能就會知道我對自動測試非常著迷。無論如何,有太多的Django專案沒有寫任何測試。這是需要預先花費一點時間去做的事情,但是卻給未來帶來巨大的紅利。如果你曾經使用print語句除錯過你的應用,在恰當的地方用合適的測試,這樣就將給你節省許多時間。

對於Django,Python的單元測試模組完全夠用了。下面是一個app的一個最小的測試例子:

import datetimefrom django.test import TestCasefrom myapp.models import Postclass BlogPostTestCase(TestCase):
    def setUp(self):
        Post.objects.create(id=1, 
            title='Starting a Django 1.6 Project the Right Way', 
            date=datetime.datetime.now(),
            category='Django')
        Post.objects.create(id=2, 
            title='Python\'s Hardest Problem', 
            date=datetime.datetime.now(),
            category='Python')

    def test_posts_have_category(self):
        """Animals that can speak are correctly identified"""
        first_post = Post.objects.get(id=1)
        second_post = Post.objects.get(id=2)
        self.assertEqual(first_post.category, 'Django')
        self.assertEqual(second_post.category, 'Python')

你可以將這些程式碼寫到名為test_<appname>.py的檔案中,並將其放到app測試時所在的目錄。為了為app執行這些測試,只需執行./manage.py test <appname>。我們建立的fabfile檔案已經知道在部署前執行這些測試,所以不需要再做任何別的修改了。

享受你的新的Django應用程式

就是這些!你已經開始了實際的開發。現在真正的樂趣才會開始。只需記住:經常提交,測試一切,還有不要在你提供服務的地方書寫程式碼。無論從現在起會發生什麼,你肯定已經以正確的方式開始了一個Django1.6 專案!

以正確的方式開始一個 Django 1.6 專案原文請看這裡

推薦文章