编写你的第一个 Django 补丁

介绍

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

回报 Django 这件事本身就是使你的顾虑得到解决的最好方式。一开始这可能会使你怯步,但这是一条有文档、工具和社区支持的成功之路。整个过程中我们会一步一步为你解说,所以你可以通过例子学习。

这个教程适合谁?

参见

如果你正在寻找关于代码贡献的细节参考,参见 编写代码 文档。

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

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

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

从哪里获得帮助:

If you're having trouble going through this tutorial, please post a message on the Django Forum, django-developers, or drop by #django-dev on irc.libera.chat to chat with other Django users who might be able to help.

这个指南涵盖哪些内容?

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

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

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

必须 Python 3!

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

对于 Windows 用户

参见 安装Python 于 Windows 的文档以获得其它指导。

代码规范

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

安装 Git

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

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

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

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

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

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

$ git clone https://github.com/YourGitHubName/django.git
...\> git clone https://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

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

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

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

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

使用 Django 本地副本创建项目

这对你测试本地 Django 项目发生了那些变化很有帮助。首先,你需要创建一个新的虚拟环境, 在可编辑模式下安装之前克隆的 Django 本地副本, 接着在你本地 Django 副本之外创建一个新的 Django 项目。 在你的新项目中,一旦你改动任何文件,你都会立刻看到相关信息,这对你编写第一个补丁是很有帮助的。

首先运行 Django 的测试套件

在为 Django 做出贡献时,非常重要的一点是,你所做的代码更改不要将错误引入 Django 的其他区域。进行更改后,检查 Django 仍然正常工作的一种方法是运行 Django 的测试套件。如果所有测试仍然通过,那么你可以合理地确定所做的更改有效并且没有破坏 Django 的其他部分。如果你以前从未运行过 Django 的测试套件,则最好事先运行一次以熟悉其输出。

运行测试套件前,使用 cd tests 命令进入 Django tests/ 目录,运行以下命令安装测试依赖:

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

如果你在安装过程中遇到错误,你的系统可能缺少一个或多个 Python 包的依赖。请查阅失败软件包的文档,或者用你遇到的错误信息在网上搜索。

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

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

接下来,需要耐心等待一段时间,因为 Django 的整个测试套件有数千个测试项,一般需要几分钟的时间才能运行结束。

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

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

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

请注意,最新的 Django “main” 分支不一定是稳定的。当使用 “main” 进行开发时,你可以检查 Django 的持续集成构建 ,以确定故障是针对你的机器还是在 Django 的官方构建中也存在。如果你点击查看一个特定的构建,你可以查看 “配置矩阵”,它显示了按 Python 版本和数据库后端细分的故障。

备注

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

尝试搞定一项新功能

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

Ticket #99999 -- 允许发表祝辞

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

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

为你的补丁创建一个分支

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

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

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

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

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

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

现在看我们的操作示例。

为工单 #99999 写测试

为了解决这个问题工单,我们将在 django.shortcuts 模块中添加一个 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'

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

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

  • 浏览 编写并运行测试 大致看一下如何在 Django 中编写测试。
  • 深入理解 Python(一本针对 Python 初学者的免费在线书籍),包含了不错的 introduction to Unit Testing
  • 读到这里,你还想深入了解的话,可以查看 Python unittest

运行你的新测试

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

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

If the tests ran correctly, you should see one failure corresponding to the test method we added, with this error:

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

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

为你的工单编写代码

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

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

def make_toast():
    return "toast"

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

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

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

第二次运行 Django 测试套件

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

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

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

书写文档

This is a new feature, so it should be documented. Open the file docs/topics/http/shortcuts.txt and add the following at the end of the file:

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

.. function:: make_toast()

.. versionadded:: 2.2

Returns ``'toast'``.

Since this new feature will be in an upcoming release it is also added to the release notes for the next version of Django. Open the release notes for the latest version in docs/releases/, which at time of writing is 2.2.txt. Add a note under the "Minor Features" header:

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

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

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

预览你的修改

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

$ 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 键返回到命令行。如果补丁内容看起来没问题,可以提交这些修改了。

提交补丁中的修改

为了提交这些修改:

$ git commit
...\> git commit

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

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

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

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

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

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

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

下一步

恭喜,你已经学会了如何为 Django 创建 pull request!如需获知更多高级技巧,参考 使用 Git 和 GitHub 工作

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

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

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

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

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

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

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

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

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