由于我们刚刚完成了很多变更,现在将是一个提交的好时间。你得适应频繁的提交,要知道,提交的粒度越小,在出错时回退的自由度越高。
要进行提交操作,让我们看看都有那些修改。
(git)
1
2
3
4
5
6
7
8
9
10
11
|
$ 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)
1
2
3
4
5
|
$ 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 模式。例如,我的文件内容多半就是:
?
在我们提交之前,还有一个信息需要查看:我们已经安装的 Python 包。我们希望能够追踪使用到的 Python 包的名称和版本,这样一来,我们就可以轻松的重建生产环境。pip 正好有个命令可以完成我们这个需求。
1
|
$ pip freeze > requirements.txt |
我将 pip 的输出存入名为 requirements.txt 的文件,并将这个文件添加到代码控制中。这样,我们总是拥有一个更新的列表,里面包含了将使用的包。
现在将 settings.py 及 requirements.txt 添加到提交文件中,并提交:
1
2
|
$ (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:你创建的应用
这可以更容易的看出哪些是你使用的第三方应用,哪些是项目自身的。仅仅记住最后有一行和下面相似的代码:
1
|
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我们想用它做迁移:
1
|
$ python manage.py schemamigration myapp --initial |
这将创建一个移植文件,用来应用我们对于数据库的模型更改(如果我们有的话),而不需要完全销毁再重建它。当情况偏离时,它也可以让我们用来 恢复更改。我们使用移植文件来 移植数据库的变化(即使还没有变化),命令如下:
1
|
$ python manage.py migrate myapp |
South足够智能,知道去哪里找到移植文件,也记得我们做的最后的移植。你可以指定单独的移植文件,但这一般并不是必须的。
当我们最终对模型做出改变时,我们使用下面的命令来让South创建一个移植文件:
1
|
$ python manage.py schemamigration myapp --auto |
这将检测myapp中的模型,并自动相应的添加、删除或修改数据库中的表。然后使用如上的移植命令就可以将改变应用到数据库上。
开发区域
还有一件事你需要注意:将开发区域与你已经确认的文件区分开,原因显而易见。使用Git和Mercurial实现这个很简单,而且也有助于部署。创建django_project所在目录之外的一个目录作为你开发区域(我把它叫做dev)。
在你的开发目录,使用git或Mercurial克隆当前项目:
1
|
$ (git /hg ) clone /path/to/my/project/ |
两个工具都将创建库的一份完整拷贝。所有的更改、分支及历史都将在新库中可用。从现在起,你应该在你的开发目录工作。
由于使用Git和Mercurial来进行分支都容易便捷,当你切换新分支或站点的大规模变化时创建分支。下面是两个工具的实现方法:
(git)
1
|
$ git checkout -b <branchname> |
这不仅创建了一个命名新分支且会将代码检出。几乎所有的开发工作都应该在分支上,这样主分支可以随时恢复。
(Mercurial)
1
|
$ hg branch <branchname> |
请注意,在Mercurial社区里分支是一个有争议的话题,目前这里有一些可用的选项,但其中还没有“显然正确”的。在这里,我使用命名分支,这可能是最安全且最有益的分支风格。任何在branch命令后的提交将在新分支生效。
使用 Fabric 来进行部署
那么我们就有了一个Django应用。我们怎么来部署它呢?Fabric。对一个合理大小的项目来说,讨论任何其它的东西都是浪费时间。Fabric可用于许多种不同目的,不过在部署方面它确实很出色。
1
|
$ pip install fabric |
Fabric 需要一个名为fabfile.py的 fabfile 文件,这个文件定义了所有我们可以采用的动作。现在我们就来创建它。将下面这些内容写入fabfile.py并将其放到项目的根目录。
1
2
3
|
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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,该怎样实际部署呢?很简单。只需运行:
1
|
$ fab prepare_deployment$ fab deploy |
在技术层面,这些可以合并为一个单独的命令,但是我觉得最好明确的准备你的部署工作再部署它,因为这样就使你更关注于你正在做的事情。
建立单元测试
如果你但凡听说过我,可能就会知道我对自动测试非常着迷。无论如何,有太多的Django项目没有写任何测试。这是需要预先花费一点时间去做的事情,但是却给未来带来巨大的红利。如果你曾经使用print语句调试过你的应用,在恰当的地方用合适的测试,这样就将给你节省许多时间。
对于Django,Python的单元测试模块完全够用了。下面是一个app的一个最小的测试例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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 项目!