{"title":"编写你的第一个 Django 补丁","version":"2.0","locale":"zh-hans","docname":"intro/contributing","url":"/zh-hans/2.0/intro/contributing/","canonical":"https://djangodocs.dev/zh-hans/2.0/intro/contributing/","summary":"想为 Django 社区做一点贡献？也许是你发现了一个想修复的 bug，或者想添加一个新的功能。回报 Django 这件事本身就是使你的顾虑得到解决的最好方式。","html":"<h1>编写你的第一个 Django 补丁<a class=\"heading-anchor\" href=\"#writing-your-first-patch-for-django\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h1>\n<section id=\"introduction\">\n<h2>介绍<a class=\"heading-anchor\" href=\"#introduction\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>想为 Django 社区做一点贡献？也许是你发现了一个想修复的 bug，或者想添加一个新的功能。</p>\n<p>回报 Django 这件事本身就是使你的顾虑得到解决的最好方式。一开始这可能会使你怯步，但这真的很简单。整个过程中我们会一步一步为你解说，所以你可以通过例子学习。</p>\n<section id=\"who-s-this-tutorial-for\">\n<h3>这个教程适合谁？<a class=\"heading-anchor\" href=\"#who-s-this-tutorial-for\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<aside class=\"admonition admonition-seealso\" role=\"note\">\n<p class=\"admonition-title\">See also</p>\n<p>如果你正在寻找一个关于如何提交补丁的说明文档，请查看 <a class=\"reference internal\" href=\"/zh-hans/2.0/internals/contributing/writing-code/submitting-patches/\"><span class=\"doc\">Submitting patches</span></a>。</p>\n</aside>\n<p>使用教程前，我们希望你至少对于 Django 的运行方式有一定的认识。 这意味着你可以很容易地通读 <a class=\"reference internal\" href=\"/zh-hans/2.0/intro/tutorial01/\"><span class=\"doc\">编写第一个 Django 应用</span></a>。 除此之外，你应该对于 Python 有很好的理解。 如果不太熟悉 Python，我们为您推荐  <a class=\"reference external\" href=\"http://www.diveintopython3.net/\">Dive Into Python</a>，对于初学Python的程序员来说这是一本很棒（而且免费）的在线电子书。</p>\n<p>那些不熟悉版本控制系统及缺陷跟踪的朋友可以查看这个教程，这个链接包含了足够的信息。如果你打算定期地为 Django 做贡献，你可能期望阅读更多关于这些不同工具的资料。</p>\n<p>当然对于此教程中的大部分内容，Django 会尽可能做出解释以帮助广大的读者。</p>\n<aside class=\"admonition-where-to-get-help admonition\" role=\"note\">\n<p class=\"admonition-title\">从哪里获得帮助：</p>\n<p>如果你在使用本教程时遇到困难, 你可以发信息给 <a class=\"reference internal\" href=\"/zh-hans/2.0/internals/mailing-lists/#django-developers-mailing-list\"><span class=\"std std-ref\">django-developers</span></a> 中的人或登陆 <a class=\"reference external\" href=\"irc://irc.freenode.net/django-dev\">#django-dev on irc.freenode.net</a> 向其他 Django 使用者寻求帮助。</p>\n</aside>\n</section>\n<section id=\"what-does-this-tutorial-cover\">\n<h3>这个指南涵盖哪些内容？<a class=\"heading-anchor\" href=\"#what-does-this-tutorial-cover\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>我们将指导你贡献你的第一个 Django 补丁，在本教程完毕时，你将对相关工具及流程有一个基本的认识。特别的，我们将覆盖以下内容：</p>\n<ul class=\"simple\">\n<li><p>安装 Git。</p></li>\n<li><p>如何下载 Django 的开发版。</p></li>\n<li><p>运行 Django 的测试套件。</p></li>\n<li><p>为你的补丁写一个测试。</p></li>\n<li><p>为你的补丁编写代码。</p></li>\n<li><p>测试你的补丁。</p></li>\n<li><p>提交一个 pull request（PR）。</p></li>\n<li><p>在哪里查找更多的信息。</p></li>\n</ul>\n<p>一旦你完成了这份教程，你可以浏览 <a class=\"reference internal\" href=\"/zh-hans/2.0/internals/contributing/\"><span class=\"doc\">Django 贡献文档</span></a> 的剩余部分。它包含了大量信息。任何想成为 Django 的正式贡献者的人都必须阅读它。如果你有问题，它也许会给你答案。</p>\n<aside class=\"admonition-python-3-required admonition\" role=\"note\">\n<p class=\"admonition-title\">必须Python 3！</p>\n<p>目前的 Django 版本不再支持 Python 2.7。你可以在 <a class=\"reference external\" href=\"https://www.python.org/downloads/\">Python 下载页</a> 或通过操作系统的包管理器下载 Python 3。</p>\n</aside>\n<aside class=\"admonition-for-windows-users admonition\" role=\"note\">\n<p class=\"admonition-title\">对于 Windows 用户</p>\n<p>在Windows上安装 Python 时，确保勾选在环境变量path中“添加 python.exe 的路径”，这样才可以让它在命令行下总是可用。</p>\n</aside>\n</section>\n</section>\n<section id=\"code-of-conduct\">\n<h2>代码规范<a class=\"heading-anchor\" href=\"#code-of-conduct\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>作为一个贡献者, 你可以帮助我们保持 Django 的社区开放性和包容性。请仔细阅读并遵守我们的 <a class=\"reference external\" href=\"https://www.djangoproject.com/conduct/\">行为守则</a>。</p>\n</section>\n<section id=\"installing-git\">\n<h2>安装Git<a class=\"heading-anchor\" href=\"#installing-git\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>在本教程中，你需要安装好 Git，用 Git 下载 Django 的最新开发版本并且为你的修改生成补丁文件。</p>\n<p>要检查你是否已经安装 Git，命令行输入 <code class=\"docutils literal notranslate\">git</code>。如果提示这个命令无法找到，你必须下载并安装它，参考 <a class=\"reference external\" href=\"https://git-scm.com/download\">Git's download page</a>。</p>\n<aside class=\"admonition-for-windows-users admonition\" role=\"note\">\n<p class=\"admonition-title\">对于 Windows 用户</p>\n<p>在 Windows 上安装 Git， 我们推荐勾选 &quot;Git Bash&quot; 选项， 以便 Git 能在它的独立 shell 下正常运行。本教程假定你这就是这样安装的。</p>\n</aside>\n<p>如果你还不熟悉 Git, 你可以在命令行下输入 <code class=\"docutils literal notranslate\">git help</code>  了解更多关于 Git 命令的使用方法 (确保已安装)</p>\n</section>\n<section id=\"getting-a-copy-of-django-s-development-version\">\n<h2>获得一个 Django 开发版本的副本<a class=\"heading-anchor\" href=\"#getting-a-copy-of-django-s-development-version\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>为 Django 做贡献的第一步就是获取源代码副本。首先， fork Github 上的 Django 项目 &lt;https://github.com/django/django/fork&gt;。接下来，在命令行中，使用 <code class=\"docutils literal notranslate\">cd</code> 命令切换至某个你想存放 Django 源码的目录。</p>\n<p>使用下面的命令来下载 Django 的源码库：</p>\n<div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>git clone git@github.com:YourGitHubName/django.git\n</code></pre></div>\n<p>现在，你有了 Django 的本地副本，你可以安装它，就像你用 <code class=\"docutils literal notranslate\">pip</code> 安装其它软件包一样。 最方便的方式是通过使用 <em>虚拟环境</em> （或 virtualenv）， 这是 Python 的内置功能，可以将每个项目安装的软件包放在不同的目录内，确保它们不会互相干扰。</p>\n<p>将你所有的 virtualenvs 保存在同一个地方是个不错的主意，例如你主目录下的 <code class=\"docutils literal notranslate\">.virtualenvs/</code>。如果这个文件夹不存在的话，创建它：</p>\n<div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>mkdir ~/.virtualenvs\n</code></pre></div>\n<p>现在创建新的虚拟运行环境</p>\n<div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>python3 -m venv ~/.virtualenvs/djangodev\n</code></pre></div>\n<p>该路径就是保存这个新的虚拟运行环境的地方。</p>\n<aside class=\"admonition-for-windows-users admonition\" role=\"note\">\n<p class=\"admonition-title\">对于 Windows 用户</p>\n<p>如果你在 Windows 上使用 Git Bash shell，内置的 <code class=\"docutils literal notranslate\">venv</code> 模块将不会生效。因为激活脚本仅创建了系统 shell (<code class=\"docutils literal notranslate\">.bat</code>) 和 PowerShell (<code class=\"docutils literal notranslate\">.ps1</code>) 的版本。 转而使用下面安装的 <code class=\"docutils literal notranslate\">virtualenv</code> ：</p>\n<div class=\"code-block\" data-language=\"none\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">None</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"None code\"><code>$ pip install virtualenv\n$ virtualenv ~/.virtualenvs/djangodev\n</code></pre></div>\n</aside>\n<aside class=\"admonition-for-ubuntu-users admonition\" role=\"note\">\n<p class=\"admonition-title\">Ubuntu 用户</p>\n<p>在 Ubuntu 的某些版本使用以上命令可能会失败。可以换成 <code class=\"docutils literal notranslate\">virtualenv</code> 这个包名试试，不过，你要先确保已安装 <code class=\"docutils literal notranslate\">pip3</code>：</p>\n<div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>sudo apt-get install python3-pip\n<span class=\"gp\">$ </span><span class=\"c1\"># Prefix the next command with sudo if it gives a permission denied error</span>\n<span class=\"gp\">$ </span>pip3 install virtualenv\n<span class=\"gp\">$ </span>virtualenv --python<span class=\"o\">=</span><span class=\"sb\">`</span>which python3<span class=\"sb\">`</span> ~/.virtualenvs/djangodev\n</code></pre></div>\n</aside>\n<p>最后一步是激活你的 virtualenv 设置：</p>\n<div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span><span class=\"nb\">source</span> ~/.virtualenvs/djangodev/bin/activate\n</code></pre></div>\n<p>如果 <code class=\"docutils literal notranslate\">source</code> 命令不可用，你可以试试 . ：</p>\n<div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>. ~/.virtualenvs/djangodev/bin/activate\n</code></pre></div>\n<aside class=\"admonition-for-windows-users admonition\" role=\"note\">\n<p class=\"admonition-title\">对于 Windows 用户</p>\n<p>在 Windows 上激活你的 virtualenv，运行：</p>\n<div class=\"code-block\" data-language=\"none\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">None</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"None code\"><code>$ source ~/virtualenvs/djangodev/Scripts/activate\n</code></pre></div>\n</aside>\n<p>在你打开一个终端前，你必须先激活 virtualenv。 <a class=\"reference external\" href=\"https://virtualenvwrapper.readthedocs.io/en/latest/\">virtualenvwrapper</a> 是个不错的工具，简化了此操作。</p>\n<p>从现在开始你通过 <code class=\"docutils literal notranslate\">pip</code> 安装的软件包都将被安装在新的 virtualenv ，与其他环境和系统级包隔离。此外，命令行上显示了当前激活的 virtualenv 的名称，以帮助您跟踪您正在使用哪个环境。来吧，安装之前克隆的 Django 副本：</p>\n<div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>pip install -e /path/to/your/local/clone/django/\n</code></pre></div>\n<p>现在安装的 Django 版本就是你本地副本的版本。你将立刻见到任何你对它的修改，这对你编写第一个补丁很有帮助。</p>\n</section>\n<section id=\"rolling-back-to-a-previous-revision-of-django\">\n<h2>回滚至之前的 Django 版本<a class=\"heading-anchor\" href=\"#rolling-back-to-a-previous-revision-of-django\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>这个教程中，我们使用工单 <a class=\"extlink-ticket reference external\" href=\"https://code.djangoproject.com/ticket/24788\">#24788</a> 来作为学习用例，所以我们回顾 git 中 Django 的版本历史，找到一个这个问题的补丁没有提交之前的版本。 这样的话我们就可以参与到从草稿到完成补丁的全过程，包括运行 Django 的测试套件。</p>\n<p><strong>请记住，我们将用 Django 的老版本来到达学习的目的，通常情况下你应当使用当前最新的开发版本来提交补丁并解决工单！</strong></p>\n<aside class=\"admonition admonition-note\" role=\"note\">\n<p class=\"admonition-title\">Note</p>\n<p>这个补丁由 Paweł Marczewski 开发，并 Git  commit 到 Django 源码 <a class=\"reference external\" href=\"https://github.com/django/django/commit/4df7e8483b2679fc1cba3410f08960bac6f51115\">commit 4df7e8483b2679fc1cba3410f08960bac6f51115</a>。因此, 我们要回到补丁提交之前的版本号， <a class=\"reference external\" href=\"https://github.com/django/django/commit/4ccfc4439a7add24f8db4ef3960d02ef8ae09887\">commit 4ccfc4439a7add24f8db4ef3960d02ef8ae09887</a>。</p>\n</aside>\n<p>首先打开 Django 源码的根目录（这个目录包含了 <code class=\"docutils literal notranslate\">django</code>， <code class=\"docutils literal notranslate\">docs</code>， <code class=\"docutils literal notranslate\">tests</code>， <code class=\"docutils literal notranslate\">AUTHORS</code>，等等）。然后你可以 check out 下面教程要用的较早版本的 Django ：</p>\n<div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>git checkout 4ccfc4439a7add24f8db4ef3960d02ef8ae09887\n</code></pre></div>\n</section>\n<section id=\"running-django-s-test-suite-for-the-first-time\">\n<h2>首先运行 Django 的测试套件<a class=\"heading-anchor\" href=\"#running-django-s-test-suite-for-the-first-time\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>当你贡献代码给 Django 的时候，你修改的代码千万不要给其它部分引入新的 bug。 有个办法可以在你更改代码之后检查 Django 是否能正常工作，就是运行 Django 的测试套件。如果所有的测试用例都通过，你就有理由相信你的改动完全没有破坏 Django。如果你从来没有运行过 Django 的测试套件，那么比较好的做法是事先运行一遍，熟悉下正常情况下应该输出什么结果。</p>\n<p>运行测试套件之前，先 <code class=\"docutils literal notranslate\">cd</code> 进入 Django 的 <code class=\"docutils literal notranslate\">test/</code> 目录，安装其依赖，运行：</p>\n<div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>pip install -r requirements/py3.txt\n</code></pre></div>\n<p>如果安装过程中发生了错误，可能是你的系统缺少一个或多个 Python 依赖包。请参考安装失败的包的文档或者在网上搜索提示的错误信息。</p>\n<p>现在你可以运行测试套件。如果你用的是 GNU/Linux， macOS 或者其它类 Unix 系统，运行：</p>\n<div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>./runtests.py\n</code></pre></div>\n<p>现在坐下来放松一下。Django 完整的测试套件有超过 9600 种不同的测试，所以它需要运行 5 到 15 分钟，这取决于你的电脑的速度。</p>\n<p>Django 的测试套件运行时，您将看到一个字符流代表每个测试的运行的状态。 <code class=\"docutils literal notranslate\">E</code> 表示测试中出现异常， <code class=\"docutils literal notranslate\">F</code> 表示断言失败。这两种情况都被认为测试失败， <code class=\"docutils literal notranslate\">x</code> 和 <code class=\"docutils literal notranslate\">s</code> 分别表示与期望结果不同和跳过测试，点表示测试通过。</p>\n<p>缺失外部依赖库通常会导致测试被跳过；查看 <a class=\"reference internal\" href=\"/zh-hans/2.0/internals/contributing/writing-code/unit-tests/#running-unit-tests-dependencies\"><span class=\"std std-ref\">Running all the tests</span></a> 获取依赖库列表，如果你修改了测试代码，请同时安装相关依赖库（本教程无需额外依赖库）。某些测试使用了特定的数据库后端，如果当前测试设置并未使用此数据库后端，那么这些相关的测试也会被跳过。SQLite 是默认的数据库后端。如果想使用其他后端进行测试，查看 <a class=\"reference internal\" href=\"/zh-hans/2.0/internals/contributing/writing-code/unit-tests/#running-unit-tests-settings\"><span class=\"std std-ref\">Using another settings module</span></a>。</p>\n<p>一旦测试完成，你将被告知测试通过与否。正因你尚未修改 Django 代码，整个测试套件 <strong>应该</strong> 成功通过。如果遇到失败或错误，请确认是否遵照了之前所有的步骤。查看 <a class=\"reference internal\" href=\"/zh-hans/2.0/internals/contributing/writing-code/unit-tests/#running-unit-tests\"><span class=\"std std-ref\">Running the unit tests</span></a> 了解更多。如果你使用的是 Python 3.5+，可能会产生一些和 deprecation 警告相关的失败，可忽略。这些失败已在 Django 中得到修复。</p>\n<p>注意最新版本 Django 分支不总是稳定的。当在分支上开发时，你可以查看代码持续集成构建页面的信息 <a class=\"reference external\" href=\"https://djangoci.com\">Django's continuous integration builds</a> 来判断测试错误只在你指定的电脑上发生，还是官方版本中也存在该错误。如果点击某个构建信息，可以通过 &quot;Configuration Matrix&quot; 查看错误发生时 Python 以及后端数据库的信息。</p>\n<aside class=\"admonition admonition-note\" role=\"note\">\n<p class=\"admonition-title\">Note</p>\n<p>在本教程以及处理工单所用分支中，测试使用数据库 SQLite 即可，然而在某些情况下需要（有时需要） ，参考 :ref:<a href=\"#id1\"><span class=\"problematic\" id=\"id2\">`</span></a>run the tests using a different database <a href=\"#id3\"><span class=\"problematic\" id=\"id4\">`</span></a>。</p>\n</aside>\n</section>\n<section id=\"creating-a-branch-for-your-patch\">\n<h2>为你的补丁创建一个分支<a class=\"heading-anchor\" href=\"#creating-a-branch-for-your-patch\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>在做出任何修改之前，为你的工单创建一个分支：</p>\n<div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>git checkout -b ticket_24788\n</code></pre></div>\n<p>你可以选择任何名称来命名你的分支，&quot;ticket_24788&quot; 是一个例子。所有在这个分支的修改只会对特定的工单生效，不会影响我们之前克隆的主分支。</p>\n</section>\n<section id=\"writing-some-tests-for-your-ticket\">\n<h2>为你的工单写一些测试用例<a class=\"heading-anchor\" href=\"#writing-some-tests-for-your-ticket\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>大多数情况下，Django 的补丁必需包含测试。Bug 修复补丁的测试是一个回归测试，确保该 Bug 不会再次在 Django 中出现。该测试应该在 Bug 存在时测试失败，在 Bug 已经修复后通过测试。新功能补丁的测试必须验证新功能是否正常运行。新功能的测试将在功能正常时通过测试，功能未执行时测试失败。</p>\n<p>最好的方式是在修改代码之前写测试单元代码。这种开发风格叫做 <a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Test-driven_development\">test-driven development</a> 被应用在项目开发和单一补丁开发过程中。单元测试编写完毕后，执行单元测试，此时测试失败（因为目前还没有修复 bug 或添加新功能），如果测试成功通过，你需要重新修改单元测试保证测试失败。因为单元测试并不能阻止 bug 发生。</p>\n<p>现在看我们的操作示例。</p>\n<section id=\"writing-some-tests-for-ticket-24788\">\n<h3>为工单 #24788 写测试<a class=\"heading-anchor\" href=\"#writing-some-tests-for-ticket-24788\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>工单 <a class=\"extlink-ticket reference external\" href=\"https://code.djangoproject.com/ticket/24788\">#24788</a> 提议增加小特性：为表单类指定类级别的属性 <code class=\"docutils literal notranslate\">prefix</code>，以便:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code>[…] forms which ship with apps could effectively namespace themselves such\nthat N overlapping form fields could be POSTed at once and resolved to the\ncorrect form.\n</code></pre></div>\n<p>为解决此工单，我们为 <code class=\"docutils literal notranslate\">BaseForm</code> 类添加了 <code class=\"docutils literal notranslate\">prefix</code> 属性。当创建该类实例时， 向 <code class=\"docutils literal notranslate\">__init__()</code> 方法传递 prefix 时，设置的仍然是实例属性 prefix。当不传递 prefix（或者传递值为 <code class=\"docutils literal notranslate\">None</code>）时，将会使用类级别的 prefix。 在我们作出修改前，我们先写一些测试来验证我们修改的功能无误，进而确保它在将来也不会出现问题。</p>\n<p>前往 Django 的 <code class=\"docutils literal notranslate\">tests/forms_tests/tests/</code> 目录并打开 <code class=\"docutils literal notranslate\">test_forms.py</code> 文件。在 1674 行附近的 <code class=\"docutils literal notranslate\">test_forms_with_null_boolean</code> 函数之前插入以下代码：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">def</span> <span class=\"nf\">test_class_prefix</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n    <span class=\"c1\"># Prefix can be also specified at the class level.</span>\n    <span class=\"k\">class</span> <span class=\"nc\">Person</span><span class=\"p\">(</span>Form<span class=\"p\">):</span>\n        first_name <span class=\"o\">=</span> CharField<span class=\"p\">()</span>\n        prefix <span class=\"o\">=</span> <span class=\"s1\">&#39;foo&#39;</span>\n\n    p <span class=\"o\">=</span> Person<span class=\"p\">()</span>\n    <span class=\"bp\">self</span><span class=\"o\">.</span>assertEqual<span class=\"p\">(</span>p<span class=\"o\">.</span>prefix<span class=\"p\">,</span> <span class=\"s1\">&#39;foo&#39;</span><span class=\"p\">)</span>\n\n    p <span class=\"o\">=</span> Person<span class=\"p\">(</span>prefix<span class=\"o\">=</span><span class=\"s1\">&#39;bar&#39;</span><span class=\"p\">)</span>\n    <span class=\"bp\">self</span><span class=\"o\">.</span>assertEqual<span class=\"p\">(</span>p<span class=\"o\">.</span>prefix<span class=\"p\">,</span> <span class=\"s1\">&#39;bar&#39;</span><span class=\"p\">)</span>\n</code></pre></div>\n<p>这个新测试用来检查：按预想设定了类级别的 prefix，创建实例时传递 <code class=\"docutils literal notranslate\">prefix</code> 参数仍然有效。</p>\n<aside class=\"admonition-but-this-testing-thing-looks-kinda-hard admonition\" role=\"note\">\n<p class=\"admonition-title\">但这种测试看起来有点困难……</p>\n<p>如果你之前从未处理过测试，那他们看起来会有点难以编写。幸运的是，测试是一个计算机编程中 <em>非常</em> 大的一个主题，所以这里有大量的相关资料：</p>\n<ul class=\"simple\">\n<li><p>浏览 <a class=\"reference internal\" href=\"/zh-hans/2.0/topics/testing/overview/\"><span class=\"doc\">Writing and running tests</span></a> 大致看一下如何在 Django 中编写测试。</p></li>\n<li><p>深入理解 Python（一本针对 Python 初学者的免费在线书籍），包含了不错的 <a class=\"reference external\" href=\"http://www.diveintopython3.net/unit-testing.html\">introduction to Unit Testing</a>。</p></li>\n<li><p>读到这里，你还想深入了解的话，可以查看 Python <a class=\"reference external\" href=\"https://docs.python.org/3/library/unittest.html#module-unittest\" title=\"(in Python v3.14)\"><code class=\"xref py py-mod docutils literal notranslate\">unittest</code></a>。</p></li>\n</ul>\n</aside>\n</section>\n<section id=\"running-your-new-test\">\n<h3>运行你的新测试<a class=\"heading-anchor\" href=\"#running-your-new-test\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>事实上，我们还没有对 <code class=\"docutils literal notranslate\">BaseForm</code> 做出任何修改，所以我们的测试将会失败。让我们运行 <code class=\"docutils literal notranslate\">forms_tests</code> 下所有的测试来看看会发生什么。在命令行中使用 <code class=\"docutils literal notranslate\">cd</code> 进入 Django 的 <code class=\"docutils literal notranslate\">tests/</code> 目录并运行：</p>\n<div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>./runtests.py forms_tests\n</code></pre></div>\n<p>如果测试执行正确，你应该能看到一个错误，正好由我们刚添加的测试方法报告。如果所有测试都通过了，你应该确认下是否将上述新的测试代码加到了合适的文件夹和类里。</p>\n</section>\n</section>\n<section id=\"writing-the-code-for-your-ticket\">\n<h2>为你的工单编写代码<a class=\"heading-anchor\" href=\"#writing-the-code-for-your-ticket\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>下一步我们将把在工单 <a class=\"extlink-ticket reference external\" href=\"https://code.djangoproject.com/ticket/24788\">#24788</a> 中描述的功能添加进 Django。</p>\n<section id=\"writing-the-code-for-ticket-24788\">\n<h3>为工单 #24788 编写代码<a class=\"heading-anchor\" href=\"#writing-the-code-for-ticket-24788\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>进入 <code class=\"docutils literal notranslate\">django/django/forms/</code> 文件夹并打开 <code class=\"docutils literal notranslate\">forms.py</code> 文件。找到第 72 行的 <code class=\"docutils literal notranslate\">BaseForm</code> 类，在 <code class=\"docutils literal notranslate\">field_order</code> 属性之后添加 <code class=\"docutils literal notranslate\">prefix</code> 类属性:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span> <span class=\"nc\">BaseForm</span><span class=\"p\">:</span>\n    <span class=\"c1\"># This is the main implementation of all the Form logic. Note that this</span>\n    <span class=\"c1\"># class is different than Form. See the comments by the Form class for</span>\n    <span class=\"c1\"># more information. Any improvements to the form API should be made to</span>\n    <span class=\"c1\"># *this* class, not to the Form class.</span>\n    field_order <span class=\"o\">=</span> <span class=\"kc\">None</span>\n    prefix <span class=\"o\">=</span> <span class=\"kc\">None</span>\n</code></pre></div>\n</section>\n<section id=\"verifying-your-test-now-passes\">\n<h3>确保你的测试现在通过了<a class=\"heading-anchor\" href=\"#verifying-your-test-now-passes\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>修改 Django 源码后，我们通过之前编写的测试方法来验证源码修改是否工作正常。运行测试 <code class=\"docutils literal notranslate\">forms_tests</code> 目录下所有的测试方法， <code class=\"docutils literal notranslate\">cd</code> 进入  Django 的 <code class=\"docutils literal notranslate\">tests/</code> 目录然后运行：</p>\n<div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>./runtests.py forms_tests\n</code></pre></div>\n<p>哦，好消息是我们写了这些测试用例！不过，你仍收到一个异常:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"ne\">AssertionError</span><span class=\"p\">:</span> <span class=\"kc\">None</span> <span class=\"o\">!=</span> <span class=\"s1\">&#39;foo&#39;</span>\n</code></pre></div>\n<p>我们忘了在 <code class=\"docutils literal notranslate\">__init__</code> 方法中添加条件语句。在 <code class=\"docutils literal notranslate\">django/forms/forms.py</code> 中，更改现在在第 87 行的代码 <code class=\"docutils literal notranslate\">self.prefix = prefix</code>，添加一个条件语句:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">if</span> prefix <span class=\"ow\">is</span> <span class=\"ow\">not</span> <span class=\"kc\">None</span><span class=\"p\">:</span>\n    <span class=\"bp\">self</span><span class=\"o\">.</span>prefix <span class=\"o\">=</span> prefix\n</code></pre></div>\n<p>重新运行测试方法会通过所有测试。如果没有通过测试，请重新确认是否按要求修改了上面提到的类 <code class=\"docutils literal notranslate\">BaseForm</code>，以及新添加的测试方法是否被正确复制到指定位置。</p>\n</section>\n</section>\n<section id=\"running-django-s-test-suite-for-the-second-time\">\n<h2>第二次运行 Django 测试套件<a class=\"heading-anchor\" href=\"#running-django-s-test-suite-for-the-second-time\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>如果已经确认补丁以及测试结果都正常，就运行 Django 的测试套件，验证你的修改是否导致 Django 的其它部分引入了新的 bug。 虽然测试用例帮助识别容易被人忽略的错误，但测试通过并不能保证完全没有 bug 存在。</p>\n<p>运行 Django 完整的测试用例，<code class=\"docutils literal notranslate\">cd</code> 进入 Django下的  <code class=\"docutils literal notranslate\">tests/</code> 目录并运行：</p>\n<div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>./runtests.py\n</code></pre></div>\n<p>只要没有看到测试异常，你就可以继续下一步。</p>\n</section>\n<section id=\"writing-documentation\">\n<h2>书写文档<a class=\"heading-anchor\" href=\"#writing-documentation\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>这个新功能信息应该被记录成文档。在 <code class=\"docutils literal notranslate\">django/docs/ref/forms/api.txt</code> 第 1068 行（在文件末）添加以下部分内容:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code>The prefix can also be specified on the form class::\n\n    &gt;&gt;&gt; class PersonForm(forms.Form):\n    ...     ...\n    ...     prefix = &#39;person&#39;\n\n.. versionadded:: 1.9\n\n    The ability to specify ``prefix`` on the form class was added.\n</code></pre></div>\n<p>由于一新功能将在未来的某个版本被加入，所以 Django 1.9 的发布说明里加入了相关内容，在文件 <code class=\"docutils literal notranslate\">docs/releases/1.9.txt</code> 第 164 行 &quot;Forms&quot; 部分:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code>* A form prefix can be specified inside a form class, not only when\n  instantiating a form. See :ref:`form-prefix` for details.\n</code></pre></div>\n<p>更多关于编写文档和 <code class=\"docutils literal notranslate\">versionadded</code> 的解释和信息，请参考 <a class=\"reference internal\" href=\"/zh-hans/2.0/internals/contributing/writing-documentation/\"><span class=\"doc\">编写文档</span></a>。这个页面还介绍了怎么在本地重新生成一份文档，方便你在本地预览文档。</p>\n</section>\n<section id=\"previewing-your-changes\">\n<h2>预览你的修改<a class=\"heading-anchor\" href=\"#previewing-your-changes\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>现在让我们检查一下补丁里作出的修改。为列出当前 Django 副本（包含你的修改）和先前教程中签出的初始版本间的差异：</p>\n<div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>git diff\n</code></pre></div>\n<p>使用方向键上下移动</p>\n<div class=\"code-block\" data-language=\"diff\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Diff</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Diff code\"><code><span class=\"gh\">diff --git a/django/forms/forms.py b/django/forms/forms.py</span>\n<span class=\"gh\">index 509709f..d1370de 100644</span>\n<span class=\"gd\">--- a/django/forms/forms.py</span>\n<span class=\"gi\">+++ b/django/forms/forms.py</span>\n<span class=\"gu\">@@ -75,6 +75,7 @@ class BaseForm:</span>\n     # information. Any improvements to the form API should be made to *this*\n     # class, not to the Form class.\n     field_order = None\n<span class=\"gi\">+    prefix = None</span>\n\n     def __init__(self, data=None, files=None, auto_id=&#39;id_%s&#39;, prefix=None,\n                  initial=None, error_class=ErrorList, label_suffix=None,\n<span class=\"gu\">@@ -83,7 +84,8 @@ class BaseForm:</span>\n         self.data = data or {}\n         self.files = files or {}\n         self.auto_id = auto_id\n<span class=\"gd\">-        self.prefix = prefix</span>\n<span class=\"gi\">+        if prefix is not None:</span>\n<span class=\"gi\">+            self.prefix = prefix</span>\n         self.initial = initial or {}\n         self.error_class = error_class\n         # Translators: This is the default suffix added to form field labels\n<span class=\"gh\">diff --git a/docs/ref/forms/api.txt b/docs/ref/forms/api.txt</span>\n<span class=\"gh\">index 3bc39cd..008170d 100644</span>\n<span class=\"gd\">--- a/docs/ref/forms/api.txt</span>\n<span class=\"gi\">+++ b/docs/ref/forms/api.txt</span>\n<span class=\"gu\">@@ -1065,3 +1065,13 @@ You can put several Django forms inside one ``&lt;form&gt;`` tag. To give each</span>\n     &gt;&gt;&gt; print(father.as_ul())\n     &lt;li&gt;&lt;label for=&quot;id_father-first_name&quot;&gt;First name:&lt;/label&gt; &lt;input type=&quot;text&quot; name=&quot;father-first_name&quot; id=&quot;id_father-first_name&quot; /&gt;&lt;/li&gt;\n     &lt;li&gt;&lt;label for=&quot;id_father-last_name&quot;&gt;Last name:&lt;/label&gt; &lt;input type=&quot;text&quot; name=&quot;father-last_name&quot; id=&quot;id_father-last_name&quot; /&gt;&lt;/li&gt;\n<span class=\"gi\">+</span>\n<span class=\"gi\">+The prefix can also be specified on the form class::</span>\n<span class=\"gi\">+</span>\n<span class=\"gi\">+    &gt;&gt;&gt; class PersonForm(forms.Form):</span>\n<span class=\"gi\">+    ...     ...</span>\n<span class=\"gi\">+    ...     prefix = &#39;person&#39;</span>\n<span class=\"gi\">+</span>\n<span class=\"gi\">+.. versionadded:: 1.9</span>\n<span class=\"gi\">+</span>\n<span class=\"gi\">+    The ability to specify ``prefix`` on the form class was added.</span>\n<span class=\"gh\">diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt</span>\n<span class=\"gh\">index 5b58f79..f9bb9de 100644</span>\n<span class=\"gd\">--- a/docs/releases/1.9.txt</span>\n<span class=\"gi\">+++ b/docs/releases/1.9.txt</span>\n<span class=\"gu\">@@ -161,6 +161,9 @@ Forms</span>\n   :attr:`~django.forms.Form.field_order` attribute, the ``field_order``\n   constructor argument , or the :meth:`~django.forms.Form.order_fields` method.\n\n<span class=\"gi\">+* A form prefix can be specified inside a form class, not only when</span>\n<span class=\"gi\">+  instantiating a form. See :ref:`form-prefix` for details.</span>\n<span class=\"gi\">+</span>\n Generic Views\n ^^^^^^^^^^^^^\n\n<span class=\"gh\">diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py</span>\n<span class=\"gh\">index 690f205..e07fae2 100644</span>\n<span class=\"gd\">--- a/tests/forms_tests/tests/test_forms.py</span>\n<span class=\"gi\">+++ b/tests/forms_tests/tests/test_forms.py</span>\n<span class=\"gu\">@@ -1671,6 +1671,18 @@ class FormsTestCase(SimpleTestCase):</span>\n         self.assertEqual(p.cleaned_data[&#39;last_name&#39;], &#39;Lennon&#39;)\n         self.assertEqual(p.cleaned_data[&#39;birthday&#39;], datetime.date(1940, 10, 9))\n\n<span class=\"gi\">+    def test_class_prefix(self):</span>\n<span class=\"gi\">+        # Prefix can be also specified at the class level.</span>\n<span class=\"gi\">+        class Person(Form):</span>\n<span class=\"gi\">+            first_name = CharField()</span>\n<span class=\"gi\">+            prefix = &#39;foo&#39;</span>\n<span class=\"gi\">+</span>\n<span class=\"gi\">+        p = Person()</span>\n<span class=\"gi\">+        self.assertEqual(p.prefix, &#39;foo&#39;)</span>\n<span class=\"gi\">+</span>\n<span class=\"gi\">+        p = Person(prefix=&#39;bar&#39;)</span>\n<span class=\"gi\">+        self.assertEqual(p.prefix, &#39;bar&#39;)</span>\n<span class=\"gi\">+</span>\n     def test_forms_with_null_boolean(self):\n         # NullBooleanField is a bit of a special case because its presentation (widget)\n         # is different than its data. This is handled transparently, though.\n</code></pre></div>\n<p>当你检查完补丁后，敲击 <code class=\"docutils literal notranslate\">q</code> 键返回到命令行。如果补丁内容看起来没问题，可以提交这些修改了。</p>\n</section>\n<section id=\"committing-the-changes-in-the-patch\">\n<h2>提交补丁中的修改<a class=\"heading-anchor\" href=\"#committing-the-changes-in-the-patch\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>为了提交这些修改：</p>\n<div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>git commit -a\n</code></pre></div>\n<p>这会打开文本编辑器以便输入提交信息。参考 <a class=\"reference internal\" href=\"/zh-hans/2.0/internals/contributing/committing-code/#committing-guidelines\"><span class=\"std std-ref\">commit message guidelines</span></a> 输入类似这样的信息：</p>\n<div class=\"code-block\" data-language=\"text\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Text</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Text code\"><code>Fixed #24788 -- Allowed Forms to specify a prefix at the class level.\n</code></pre></div>\n</section>\n<section id=\"pushing-the-commit-and-making-a-pull-request\">\n<h2>推送这次提交并生成一个 pull 请求<a class=\"heading-anchor\" href=\"#pushing-the-commit-and-making-a-pull-request\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>提交补丁后，将它发送至你在 GitHub 上 fork 的仓库（用你的分支名替换 &quot;ticket_24788&quot;，如果你的分支名不是遵照教程）：</p>\n<div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>git push origin ticket_24788\n</code></pre></div>\n<p>你可以访问 <a class=\"reference external\" href=\"https://github.com/django/django/\">Django GitHub page</a> 创建一个 pull 请求。 你会在“你最近推送的分支”下看到你的分支。 单击旁边的 &quot;Compare &amp; pull request&quot;。</p>\n<p>本教程中请不要这么做。不过，在接下来显示补丁预览的页面，你可以单击 &quot;Create pull request&quot;。</p>\n</section>\n<section id=\"next-steps\">\n<h2>下一步<a class=\"heading-anchor\" href=\"#next-steps\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>恭喜，你已经学会了如何为 Django 创建 pull request！如需获知更多高级技巧，参考 <a class=\"reference internal\" href=\"/zh-hans/2.0/internals/contributing/writing-code/working-with-git/\"><span class=\"doc\">Working with Git and GitHub</span></a>。</p>\n<p>现在你可以活用这些技能帮助改善 Django 的代码库。</p>\n<section id=\"more-information-for-new-contributors\">\n<h3>针对新贡献者的更多注意事项<a class=\"heading-anchor\" href=\"#more-information-for-new-contributors\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>在你开始为 Django 编写补丁时，这里有些信息，你应该看一看：</p>\n<ul class=\"simple\">\n<li><p>确保你阅读了 Django 的参考文档 <a class=\"reference internal\" href=\"/zh-hans/2.0/internals/contributing/writing-code/submitting-patches/\"><span class=\"doc\">创建工单和提交补丁</span></a>。它涵盖了Trac 规则，如何创建自己的工单，补丁期望的代码风格和其他一些重要信息。</p></li>\n<li><p>初次提交补丁应额外阅读 <a class=\"reference internal\" href=\"/zh-hans/2.0/internals/contributing/new-contributors/\"><span class=\"doc\">首次贡献者文档</span></a>。这里有很多对新手贡献者的建议。</p></li>\n<li><p>接下来，如果你渴望更多关于为 Django 做贡献的信息，可以阅读余下的文档 <a class=\"reference internal\" href=\"/zh-hans/2.0/internals/contributing/\"><span class=\"doc\">为 Django 文档上作出贡献</span></a>。它包含了大量的有用信息，这里可以解决你可能遇到的所有问题。</p></li>\n</ul>\n</section>\n<section id=\"finding-your-first-real-ticket\">\n<h3>寻找你的第一个真正意义上的工单<a class=\"heading-anchor\" href=\"#finding-your-first-real-ticket\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>一旦你看过了之前那些信息，你便已经具备了走出困境，编写修复自己找到的工单的补丁的能力。对于那些有着“容易获得”标准的工单要尤其注意。这些工单实际上常常很简单而且对于第一次撰写补丁的人很有帮助。一旦你熟悉了给 Django 写补丁，你就可以进一步为更难且更复杂的工单写补丁。</p>\n<p>如果你只是想要简单的了解（没人会因此责备你！），那么你可以试着看看 <a class=\"reference external\" href=\"https://code.djangoproject.com/query?status=new&amp;status=reopened&amp;has_patch=0&amp;easy=1&amp;col=id&amp;col=summary&amp;col=status&amp;col=owner&amp;col=type&amp;col=milestone&amp;order=priority\">easy tickets that need patches</a> 和 <a class=\"reference external\" href=\"https://code.djangoproject.com/query?status=new&amp;status=reopened&amp;needs_better_patch=1&amp;easy=1&amp;col=id&amp;col=summary&amp;col=status&amp;col=owner&amp;col=type&amp;col=milestone&amp;order=priority\">easy tickets that have patches which need improvement</a>。如果你比较擅长写测试，那么你也可以看看这个 <a class=\"reference external\" href=\"https://code.djangoproject.com/query?status=new&amp;status=reopened&amp;needs_tests=1&amp;easy=1&amp;col=id&amp;col=summary&amp;col=status&amp;col=owner&amp;col=type&amp;col=milestone&amp;order=priority\">easy tickets that need tests</a>。一定要记得遵循在 Django 的文档声明标签和递交补丁中提到的关于声明标签的指导规则 <a class=\"reference internal\" href=\"/zh-hans/2.0/internals/contributing/writing-code/submitting-patches/\"><span class=\"doc\">声明标签和提交补丁</span></a>.</p>\n</section>\n<section id=\"what-s-next-after-creating-a-pull-request\">\n<h3>创建完 pull request，下一步做什么呢？<a class=\"heading-anchor\" href=\"#what-s-next-after-creating-a-pull-request\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>工单有了补丁后，需要他人来复审。提交 pull 请求后，为工单打上如“有补丁”，“无需测试”之类的标签，如此他人便可查找到该工单以便复审。从头开始编写补丁固然是贡献的一种方式，但复审已有补丁同样能帮助 Django。 查看 <a class=\"reference internal\" href=\"/zh-hans/2.0/internals/contributing/triaging-tickets/\"><span class=\"doc\">Triaging tickets</span></a> 了解更多。</p>\n</section>\n</section>","rootId":"writing-your-first-patch-for-django","toc":[{"title":"介绍","anchor":"introduction","children":[{"title":"这个教程适合谁？","anchor":"who-s-this-tutorial-for","children":[]},{"title":"这个指南涵盖哪些内容？","anchor":"what-does-this-tutorial-cover","children":[]}]},{"title":"代码规范","anchor":"code-of-conduct","children":[]},{"title":"安装Git","anchor":"installing-git","children":[]},{"title":"获得一个 Django 开发版本的副本","anchor":"getting-a-copy-of-django-s-development-version","children":[]},{"title":"回滚至之前的 Django 版本","anchor":"rolling-back-to-a-previous-revision-of-django","children":[]},{"title":"首先运行 Django 的测试套件","anchor":"running-django-s-test-suite-for-the-first-time","children":[]},{"title":"为你的补丁创建一个分支","anchor":"creating-a-branch-for-your-patch","children":[]},{"title":"为你的工单写一些测试用例","anchor":"writing-some-tests-for-your-ticket","children":[{"title":"为工单 #24788 写测试","anchor":"writing-some-tests-for-ticket-24788","children":[]},{"title":"运行你的新测试","anchor":"running-your-new-test","children":[]}]},{"title":"为你的工单编写代码","anchor":"writing-the-code-for-your-ticket","children":[{"title":"为工单 #24788 编写代码","anchor":"writing-the-code-for-ticket-24788","children":[]},{"title":"确保你的测试现在通过了","anchor":"verifying-your-test-now-passes","children":[]}]},{"title":"第二次运行 Django 测试套件","anchor":"running-django-s-test-suite-for-the-second-time","children":[]},{"title":"书写文档","anchor":"writing-documentation","children":[]},{"title":"预览你的修改","anchor":"previewing-your-changes","children":[]},{"title":"提交补丁中的修改","anchor":"committing-the-changes-in-the-patch","children":[]},{"title":"推送这次提交并生成一个 pull 请求","anchor":"pushing-the-commit-and-making-a-pull-request","children":[]},{"title":"下一步","anchor":"next-steps","children":[{"title":"针对新贡献者的更多注意事项","anchor":"more-information-for-new-contributors","children":[]},{"title":"寻找你的第一个真正意义上的工单","anchor":"finding-your-first-real-ticket","children":[]},{"title":"创建完 pull request，下一步做什么呢？","anchor":"what-s-next-after-creating-a-pull-request","children":[]}]}],"breadcrumbs":[{"docname":"intro/index","title":"开始","url":"/zh-hans/2.0/intro/"}],"prev":{"docname":"intro/whatsnext","title":"下一步看什么","url":"/zh-hans/2.0/intro/whatsnext/"},"next":{"docname":"topics/index","title":"Using Django","url":"/zh-hans/2.0/topics/"},"formats":{"html":"/zh-hans/2.0/intro/contributing/","markdown":"/zh-hans/2.0/intro/contributing.md","json":"/zh-hans/2.0/intro/contributing.json"},"source":"https://github.com/django/django/blob/stable/2.0.x/docs/intro/contributing.txt","official":"https://docs.djangoproject.com/zh-hans/2.0/intro/contributing/","inVersions":["6.1","6.0","5.2","5.1","5.0","4.2","4.1","4.0","3.2","3.1","3.0","2.2","2.1","2.0"],"inLocales":["en","zh-hans","fr","ja","id","pt-br","ko","es","el","pl"]}