编写你的第一个 Django 补丁P

介绍P

想为 Django 社区做一点贡献?也许是你发现了一个想修复的 bug,或者想添加一个新的功能。

回报 Django 这件事本身就是使你的顾虑得到解决的最好方式。一开始这可能会使你怯步,但这真的很简单。整个过程中我们会一步一步为你解说,所以你可以通过例子学习。

这个教程适合谁?P

参见

如果你正在寻找一个关于如何提交补丁的说明文档,请查看 Submitting patches

使用教程前,我们希望你至少对于 Django 的运行方式有一定的认识。 这意味着你可以很容易地通读 编写第一个 Django 应用。 除此之外,你应该对于 Python 有很好的理解。 如果不太熟悉 Python,我们为您推荐 `Dive Into Python`__ 对于初学Python的程序员来说这是一本很棒(而且免费)的在线电子书。

那些不熟悉版本控制系统及缺陷跟踪的朋友可以查看这个教程,这个链接包含了足够的信息。如果你打算定期地为 Django 做贡献,你可能期望阅读更多关于这些不同工具的资料。

当然对于此教程中的大部分内容,Django 会尽可能做出解释以帮助广大的读者。

从哪里获得帮助:

如果你在使用本教程时遇到困难, 你可以发信息给 django-developers 中的人或登陆 `#django-dev on irc.freenode.net`__ 向其他 Django 使用者寻求帮助。

这个指南涵盖哪些内容?P

我们将指导你贡献你的第一个 Django 补丁,在本教程完毕时,你将对相关工具及流程有一个基本的认识。特别的,我们将覆盖以下内容:

  • 安装 Git。
  • 下载一份Django开发版的拷贝。
  • 运行 Django 的测试套件。
  • 为你的补丁写一个测试。
  • 为你的补丁编写代码。
  • 测试你的补丁。
  • 提交一个 pull request(PR)。
  • 在哪里查找更多的信息。

一旦你完成了这份教程,你可以浏览 Django 贡献文档 的剩余部分。它包含了大量信息。任何想成为 Django 的正式贡献者的人都必须阅读它。如果你有问题,它也许会给你答案。

必须Python 3!

目前的 Django 版本不再支持 Python 2.7。你可以在 Python 下载页 或通过操作系统的包管理器下载 Python 3。

对于 Windows 用户

在Windows上安装 Python 时,确保勾选在环境变量path中“添加 python.exe 的路径”,这样才可以让它在命令行下总是可用。

代码规范P

作为一个贡献者, 你可以帮助我们保持 Django 的社区开放性和包容性。请仔细阅读并遵守我们的 行为守则

安装GitP

在本教程中,你需要安装好 Git,用 Git 下载 Django 的最新开发版本并且为你的修改生成补丁文件。

要检查你是否已经安装 Git,命令行输入 git。如果提示这个命令无法找到,你必须下载并安装它,参考 `Git's download page`__

如果你还不熟悉 Git, 你可以在命令行下输入 git help 了解更多关于 Git 命令的使用方法 (确保已安装)

获得一个 Django 开发版本的副本P

为 Django 做贡献的第一步就是获取源代码副本。首先, fork Github 上的 Django 项目 <https://github.com/django/django/fork>。接下来,在命令行中,使用 cd 命令切换至某个你想存放 Django 源码的目录。

使用下面的命令来下载 Django 的源码库:

$ git clone git@github.com:YourGitHubName/django.git
...\> git clone git@github.com:YourGitHubName/django.git

低速宽带连接?

你可以在用命令 git clone 下载仓库的时候加上参数 --depth 1 来跳过 Django 的提交历史,这大约能把下载大小从250MB减少到70MB

你现在已经将Django拷贝到本地,可以像安装其他软件包一样使用 ``pip``进行安装。 最便捷的方式是通过 virtual environment, 这是 Python 的一个内置特性,它可以让你在一个目录中保持独立的软件包环境而不影响其他的项目。

将你的虚拟环境都放在一个位置是明智的做法,例如将它们放置在你主目录下的 .virtualenvs/ 中。

通过运行以下命令创建一个虚拟环境:

$ python3 -m venv ~/.virtualenvs/djangodev
...\> py -m venv %HOMEPATH%\.virtualenvs\djangodev

该路径就是保存这个新的虚拟运行环境的地方。

设置虚拟环境的最后一步是激活它;

$ source ~/.virtualenvs/djangodev/bin/activate

如果 source 命令不可用,你可以试试:

$ . ~/.virtualenvs/djangodev/bin/activate

对于 Windows 用户

在Windows下采用如下命令进行激活虚拟环境:

...\> %HOMEPATH%\.virtualenvs\djangodev\Scripts\activate.bat

每当你开启一个新的终端时,你都需要激活虚拟环境。 virtualenvwrapper__ 可以使其更便捷。

当前激活的虚拟环境的名称会被展示在命令行,这可以让你搞清楚你正在使用哪一个虚拟环境。你通过 pip 安装的任何软件包如果在安装时显示了该名称,则都会被安装到该虚拟环境中,而且这些软件包不会影响到其他虚拟环境,也不会与其他系统级的软件包发生冲突。

下一步安装之前克隆的 Django 副本:

$ pip install -e /path/to/your/local/clone/django/
...\> pip install -e \path\to\your\local\clone\django\

现在安装的 Django 版本就是你本地副本的版本。你将立刻见到任何你对它的修改,这对你编写第一个补丁很有帮助。

首先运行 Django 的测试套件P

当你贡献代码给 Django 的时候,你修改的代码千万不要给其它部分引入新的 bug。 有个办法可以在你更改代码之后检查 Django 是否能正常工作,就是运行 Django 的测试套件。如果所有的测试用例都通过,你就有理由相信你的改动完全没有破坏 Django。如果你从来没有运行过 Django 的测试套件,那么比较好的做法是事先运行一遍,熟悉下正常情况下应该输出什么结果。

运行测试套件之前,先 cd 进入 Django 的 test/ 目录,安装其依赖,运行:

$ pip install -r requirements/py3.txt
...\> pip install -r requirements\py3.txt

如果安装过程中发生了错误,可能是你的系统缺少一个或多个 Python 依赖包。请参考安装失败的包的文档或者在网上搜索提示的错误信息。

现在你可以运行测试套件。如果你用的是 GNU/Linux, macOS 或者其它类 Unix 系统,运行:

$ ./runtests.py
...\> runtests.py 

现在坐下来放松一下。Django 完整的测试套件有成千上万种不同的测试,所以它至少需要运行几分钟,这个时间取决于你的电脑的速度。

当Django的测试套件被执行时,您将看到一个代表测试运行状态的字符流。 其中字符 E 表示测试中出现异常, F 表示测试中的一个断言失败,这两种情况都被认为测试结果失败。而 xs 分别表示与期望结果不同和跳过测试,逗点则表示测试被通过了。

缺失外部依赖库通常会导致测试被跳过;查看 Running all the tests 获取依赖库列表,如果你修改了测试代码,请同时安装相关依赖库(本教程无需额外依赖库)。某些测试使用了特定的数据库后端,如果当前测试设置并未使用此数据库后端,那么这些相关的测试也会被跳过。SQLite 是默认的数据库后端。如果想使用其他后端进行测试,查看 Using another settings module

代码测试集当测试执行完毕后,得到反馈信息显示测试已通过,或者测试失败。 因为还没有对 Django 的代码做任何修改, 所有的测试集 应该 测试通过. 如果测试失败或出现错误,回头确认以上执行操作是否正确. 查看 Running the unit tests 获取更多信息。

注意最新版本 Django 分支不总是稳定的。当在分支上开发时,你可以查看代码持续集成构建页面的信息 来判断测试错误只在你指定的电脑上发生,还是官方版本中也存在该错误。如果点击某个构建信息,可以通过 "Configuration Matrix" 查看错误发生时 Python 以及后端数据库的信息。

注解

在本教程以及处理工单所用分支中,测试使用数据库 SQLite 即可,然而在某些情况下需要(有时需要) ,参考 :ref:`run the tests using a different database `

尝试搞定一项新功能P

这次教程中,我们将学习去完成一个名叫 "fake ticket" 的例子。下面是关于这个例子的大体构想:

Ticket #99999 -- 允许发表祝辞

Djando中需要声明一个函数django.shortcuts.make_toast(),它的返回值为'toast'。

我们现在来实现这个新功能并且做一下相关的测试。

为你的补丁创建一个分支P

在做出任何修改之前,为你的工单创建一个分支:

$ git checkout -b ticket_99999
...\> git checkout -b ticket_99999

你可以选择任意你想要的分支名,ticket_99999只是一个例子。你在该分支上做出的所有更改,将只会针对该分支即ticket_99999产生影响,而不会影响到我们早先克隆的原始代码。

为你的工单写一些测试用例P

大多数情况下,Django 的补丁必需包含测试。Bug 修复补丁的测试是一个回归测试,确保该 Bug 不会再次在 Django 中出现。该测试应该在 Bug 存在时测试失败,在 Bug 已经修复后通过测试。新功能补丁的测试必须验证新功能是否正常运行。新功能的测试将在功能正常时通过测试,功能未执行时测试失败。

最好的方式是在修改代码之前写测试单元代码。这种开发风格叫做 `test-driven development`__ 被应用在项目开发和单一补丁开发过程中。单元测试编写完毕后,执行单元测试,此时测试失败(因为目前还没有修复 bug 或添加新功能),如果测试成功通过,你需要重新修改单元测试保证测试失败。因为单元测试并不能阻止 bug 发生。

现在看我们的操作示例。

为工单 #99999 写测试P

为了解决这次的工单问题,我们将在最上层的 django 模块中添加一个函数 make_toast()。首先我们来写一个测试用例,用于测试该函数,并且验证一下它的输出项是否正确。

前往Django的 tests/shortcuts/ 文件夹,创建一个名为 test_make_toast.py 的新文件。添加如下代码:

from django.shortcuts import make_toast
from django.test import SimpleTestCase


class MakeToastTests(SimpleTestCase):
    def test_make_toast(self):
        self.assertEqual(make_toast(), 'toast')

上述测试是用来检测 make_toast() 函数是否会返回 ``'toast'` `的。

但这种测试看起来有点困难……

如果你之前从未处理过测试,那他们看起来会有点难以编写。幸运的是,测试是一个计算机编程中 非常 大的一个主题,所以这里有大量的相关资料:

运行你的新测试P

由于我们还没有对 django.shortcuts 进行任何修改,这次测试将会失败。现在让我们来运行一下 shortcuts 目录中的所有测试,以便确定它们真的都会产生失败的结果。使用 cd 命令进入Django的 tests/ 文件夹,然后运行:

$ ./runtests.py shortcuts
...\> runtests.py shortcuts

如果测试被正确执行,一个该测试方法所对应的错误将会呈现给你:

ImportError: cannot import name 'make_toast' from 'django.shortcuts'

如果所有测试都执行过了,那么你要确保在准确的文件夹和文件名中添加了上述新测试。

为你的工单编写代码P

接下来我们将添加 make_toast() 函数

打开 django/ 文件夹中的 shortcuts.py 文件,在文件末尾追加:

def make_toast():
    return 'toast'

现在我们需要保证之前所写的测试会正常通过,以便我们判断所追加的代码是否正确。再来一次,现跳转到Django的 tests/ 目录,然后执行:

$ ./runtests.py shortcuts
...\> runtests.py shortcuts

所有项目都需要正常通过测试。如果没有,检查一下你的函数是否书写正确,并且是否写在了正确的文件中。

第二次运行 Django 测试套件P

如果已经确认补丁以及测试结果都正常,就运行 Django 的测试套件,验证你的修改是否导致 Django 的其它部分引入了新的 bug。 虽然测试用例帮助识别容易被人忽略的错误,但测试通过并不能保证完全没有 bug 存在。

运行 Django 完整的测试用例,cd 进入 Django下的 tests/ 目录并运行:

$ ./runtests.py
...\> runtests.py 

书写文档P

这是一项新功能,所以应该为它建立一个说明,打开文件 docs/topics/http/shortcuts.txt ,然后在文件末尾追加下记内容:

``make_toast()``
================

.. versionadded:: 2.2

Returns ``'toast'``.

由于这一新功能将在即将到来的版本中被加入,所以下个版本的发布说明里也加入了相关内容。打开 docs/releases/2.2.txt 文件,即发布说明的最新版本文件,在小标题"Minor Features"下面添加一个说明:

:mod:`django.shortcuts`
~~~~~~~~~~~~~~~~~~~~~~~

* The new :func:`django.shortcuts.make_toast` function returns ``'toast'``.

更多关于编写文档和 versionadded 的解释和信息,请参考 编写文档。这个页面还介绍了怎么在本地重新生成一份文档,方便你在本地预览文档。

预览你的修改P

现在是时候完成我们这个分支的所有变更,准备将它们提交了,执行:

$ git add --all
...\> git add --all

然后将你当前版本(包含有你修改的内容)的拷贝和你最初在教程中取出的版本对比:

$ git diff --cached
...\> git diff --cached

使用方向键上下移动

diff --git a/django/shortcuts.py b/django/shortcuts.py
index 7ab1df0e9d..8dde9e28d9 100644
--- a/django/shortcuts.py
+++ b/django/shortcuts.py
@@ -156,3 +156,7 @@ def resolve_url(to, *args, **kwargs):

     # Finally, fall back and assume it's a URL
     return to
+
+
+def make_toast():
+    return 'toast'
diff --git a/docs/releases/2.2.txt b/docs/releases/2.2.txt
index 7d85d30c4a..81518187b3 100644
--- a/docs/releases/2.2.txt
+++ b/docs/releases/2.2.txt
@@ -40,6 +40,11 @@ database constraints. Constraints are added to models using the
 Minor features
 --------------

+:mod:`django.shortcuts`
+~~~~~~~~~~~~~~~~~~~~~~~
+
+* The new :func:`django.shortcuts.make_toast` function returns ``'toast'``.
+
 :mod:`django.contrib.admin`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~

diff --git a/docs/topics/http/shortcuts.txt b/docs/topics/http/shortcuts.txt
index 7b3a3a2c00..711bf6bb6d 100644
--- a/docs/topics/http/shortcuts.txt
+++ b/docs/topics/http/shortcuts.txt
@@ -271,3 +271,12 @@ This example is equivalent to::
         my_objects = list(MyModel.objects.filter(published=True))
         if not my_objects:
             raise Http404("No MyModel matches the given query.")
+
+``make_toast()``
+================
+
+.. function:: make_toast()
+
+.. versionadded:: 2.2
+
+Returns ``'toast'``.
diff --git a/tests/shortcuts/test_make_toast.py b/tests/shortcuts/test_make_toast.py
new file mode 100644
index 0000000000..6f4c627b6e
--- /dev/null
+++ b/tests/shortcuts/test_make_toast.py
@@ -0,0 +1,7 @@
+from django.shortcuts import make_toast
+from django.test import SimpleTestCase
+
+
+class MakeToastTests(SimpleTestCase):
+    def test_make_toast(self):
+        self.assertEqual(make_toast(), 'toast')

当你检查完补丁后,敲击 q 键返回到命令行。如果补丁内容看起来没问题,可以提交这些修改了。

提交补丁中的修改P

为了提交这些修改:

$ git commit
...\> git commit

这会打开文本编辑器以便输入提交信息。参考 commit message guidelines 输入类似这样的信息:

Fixed #99999 -- Added a shortcut function to make toast.

推送这次提交并生成一个 pull 请求P

在提交这次的修改之后,将其发送到你在GitHub上的分支(如果你使用的名称不是"ticket_99999",用你自己的分支的名称取代它 ):

$ git push origin ticket_99999
...\> git push origin ticket_99999

你可以访问 Django GitHub page 创建一个 pull 请求。 你会在“你最近推送的分支”下看到你的分支。 单击旁边的 "Compare & pull request"。

本教程中请不要这么做。不过,在接下来显示补丁预览的页面,你可以单击 "Create pull request"。

下一步P

恭喜,你已经学会了如何为 Django 创建 pull request!如需获知更多高级技巧,参考 Working with Git and GitHub

现在你可以活用这些技能帮助改善 Django 的代码库。

针对新贡献者的更多注意事项P

在你开始为 Django 编写补丁时,这里有些信息,你应该看一看:

  • 确保你阅读了 Django 的参考文档 创建工单和提交补丁。它涵盖了Trac 规则,如何创建自己的工单,补丁期望的代码风格和其他一些重要信息。
  • 初次提交补丁应额外阅读 首次贡献者文档。这里有很多对新手贡献者的建议。
  • 接下来,如果你渴望更多关于为 Django 做贡献的信息,可以阅读余下的文档 为 Django中文手册官方文档上作出贡献。它包含了大量的有用信息,这里可以解决你可能遇到的所有问题。

寻找你的第一个真正意义上的工单P

一旦你看过了之前那些信息,你便已经具备了走出困境,编写修复自己找到的工单的补丁的能力。对于那些有着“容易获得”标准的工单要尤其注意。这些工单实际上常常很简单而且对于第一次撰写补丁的人很有帮助。一旦你熟悉了给 Django 写补丁,你就可以进一步为更难且更复杂的工单写补丁。

如果你只是想要简单的了解(没人会因此责备你!),那么你可以试着看看 `easy tickets that need patches`__`easy tickets that have patches which need improvement`__。如果你比较擅长写测试,那么你也可以看看这个 `easy tickets that need tests`__。一定要记得遵循在 Django 的文档声明标签和递交补丁中提到的关于声明标签的指导规则 声明标签和提交补丁.

创建完 pull request,下一步做什么呢?P

工单有了补丁后,需要他人来复审。提交 pull 请求后,为工单打上如“有补丁”,“无需测试”之类的标签,如此他人便可查找到该工单以便复审。从头开始编写补丁固然是贡献的一种方式,但复审已有补丁同样能帮助 Django。 查看 Triaging tickets 了解更多。