• Django 2.0.1 官方文档翻译:编写你的第一个djang补丁(page 15)


    编写你的第一个djang补丁(page 15)

    介绍

    有兴趣为社区做一些贡献?可能你发现了django中的一个你想修复的bug,或者你你想添加一个小小的功能。

    回馈django就是解决你遇到的问题的最好的方法。一开始这可能会让你望而生畏,但它真的很简单。我们会带你熟悉整个过程,所以你可以通过例子来学习。

    本节教程面向的谁呢?

    另请参见
    如果你在寻找如何提交更新的参考资料,请查看文档Submitting patches(少一个链接)

    在本节教程,我们假设你对django如何工作至少有了一个基本的了解。也就是说,你应该已经熟悉了已有的编写你的第一个django app的教程。另外,你应该对python本身有很好的理解。但如果你不熟悉,Dive Into Python是一本非常棒(且免费)的为python程序员新手准备的在线电子书。

    对于不熟悉版本控系统和Trac的人来说,会发现本教程和他的链接包含足够的让你开始学习的信息。不管怎样,如果你计划定期为django贡献,你可能会想阅读更多关于这些不同工具的内容。

    不过,大部分情况下,本教程试图尽可能多的去解释,以它可以用于最广泛的读者,

    从哪里获取帮助:
    如果你在使用本教程时遇到麻烦,请提交信息给django开发者或者登陆到 #django-dev on irc.freenode.net向有可能帮助你的django使用者寻求帮助。

    本教程包含哪些内容?

    第一次,我们会带领你给django编写一个补丁,教程的最后,你应该对涉及的工具和流程有一个基本的理解。具体来说,我们会涵盖一下内容:

    • 安装Git
    • 如何下载django的开发副本
    • 运行django的测试套件
    • 为你的补丁写一个测试
    • 为你的补丁编写代码
    • 测试你的补丁
    • 提交一个pull请求
    • 去哪查找更多的信息

    一旦你完成本教程,你就可以学习剩下的 [Django’s documentation on contributing](file:///Users/resn/Desktop/django-docs-2.0-en/internals/contributing/index.html)了。它包含了大量信息,所有想变成django贡献者的人都必须阅读它。如果你遇到问题,它可能会给你答案。

    需要使用python3
    当前版本的django不再支持python2.7。去python下载页或者你的操作系统的包管理器获取python3。
    windows用户
    在windows中安装python时,去报你选中了“Add python.exe to Path”选项,这样在命令行就总是有效的了

    代码规范

    作为一个贡献者,你可以帮助我们保持django社区的开放性和包容性,请阅读我们的代码规范

    安装Git

    在本教程中,你需要安装Git以下载django当前的开发版本,并为你做的更改生成补丁文件。

    检查你是否已经安装it,在命令行中输入git。如果你得到的信息是找不到这个命令。你必须去下载并安装它,请参阅 Git下载页

    windows用户
    在windows上安装Git时,建议选择“Git Bash”选项,这样Git就会运行在它自己的shell中。本教程假定你已经安装它。

    如果你不熟悉Git,你可以通过在命令行输入git help来找出更多关于它的命令。

    获取django开发版本的一个副本

    为django做出共享的第一步是获取源代码副本。首先fork Django on GitHub。然后,在命令行,使用cd命令进入你想要存放本地django副本的目录。
    使用下面的命令下载django源代码库:

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

    现在你就有了一份django的本地副本,你可以安装它,就像你使用pip安装其他的包一样。最方便的方式是使用虚拟环境(或者virtualenv),虚拟环境是python的内置特性,它允许你为每一个项目保留一个单独的安装包目录,这样项目之间就不会互相干扰。
    有一个好主意是把你的所有virtualenv保存在一个位置。例如,在你家目录下的.virtualenvs/中。如果不存在就创建它:

    $ mkdir ~/.virtualenvs
    

    现在通过下面的命令创建一个新的virtualenv:

    python3 -m venv ~/.virtualenvs/djangodev
    

    新环境的路径位置会保存在你的计算机中

    windows用户
    如果你在windows中使用 Git Bash shell,那么内置的venv模块会无法工作。因为激活脚本仅仅支持系统shell(.bat)和PowerShell (.ps1)。可以使用virtualenv来代替它

    $ pip install virtualenv
    $ virtualenv ~/.virtualenvs/djangodev
    

    Ubuntu用户
    在某些版本的ubuntu中上面的命令可能会失败。可以使用virtualenv来替代,首先要去报你已经安装了pip3

    $ sudo apt-get install python3-pip
    $ # Prefix the next command with sudo if it gives a permission denied error
    $ pip3 install virtualenv
    $ virtualenv --python=`which python3` ~/.virtualenvs/djangodev
    

    设置vitualenv的最后一步是激活它:

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

    如果source命令无效,你可以尝试用一个点来替代:

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

    windows用户
    在windows中激活你的virtualenv,运行下面的命令:

    $ source ~/virtualenvs/djangodev/Scripts/activate
    

    无论何时,打开一个新的终端窗口,你就必须激活virtualenv。virtualenvwrapper是一个使这个操作更方便的非常有用的工具。

    从现在开始,所有你通过pip安装的包都会安装到你的新virtualenv里,与其他环境和整个系统的包隔离开来。另外,当前激活的virtualenv的名字会在命令行中显示,以帮助你监看你正在使用的那一个。继续并安装前面克隆的django副本:

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

    现在安装的django版本指向你的本地副本。你可以立刻看到你对它做的任何更改,在你在第一个补丁的时候这会有对你非常大的帮助。

    回滚到之前的django版本

    在本教程中,我们将使用#24788来作为学习用例,因此我们需要把git中django的版本回滚到问题补丁没有提交之前的版本。这会让我们参与进从头开始编写补丁的所有步骤中,包括正在运行的django测试组件。

    请记住,虽然外面将在下面的教程中使用旧的django主干版本,但在处理自己的补丁时,你应该始终使用django的当前开发版本

    注意
    这里的补丁由Paweł Marczewski编写,并已经应用于django,提交标记4df7e8483b2679fc1cba3410f08960bac6f51115,因此,我们将使用在此之前的django版本,4ccfc4439a7add24f8db4ef3960d02ef8ae09887.

    导航到djanmgo的根目录(它是包含django, docs, tests, AUTHORS等的目录)。你可以检出我们将在下面的教程中使用的django的旧版本:

    git checkout 4ccfc4439a7add24f8db4ef3960d02ef8ae09887
    

    第一次运行django的测试组件

    在为django贡献代码时,非常重要的一点就是你的代码不要为django的其他部分引入bug。一种办法就是在你做了更改之后,运行django的测试组件,检查django是否可以正常工作。如果所有的测试都通过了,那么你有理由确信你的更改完全没有破坏django。如果你以前从未运行过django的测试组件,事先运行一次,以熟悉它的输出是什么样子的,会是一个很好的注意。
    运行测试组件之前,先进入django的tests目录,并运行下面的命令安装依赖:

    $ pip install -r requirements/py3.txt
    

    如果在安装期间你突然遇到一个错误,这可能是你的系统没有安装一个或多个python包的依赖。请查阅失败包的文档或者在网上搜索你遇到的错误信息。

    现在,我们准备肉运行测试组件,如果你使用的是GNU/Linux, macOS或者其他的UYnix版本,运行下面的命令:

    $ ./runtests.py
    

    现在坐下来放松一下,django的全部测试组件有超过9600中测试,因此,他可能需要运行5到15分钟,当然,这取决于你的计算机的速度。

    当django的测试组件运行的时候,你可以看一个显示每个测试用例状态的字符串流。E 表示测试期间出现了一个错误,F 表示测试的断言失败。这两种情况都被认为是测试失败。同时,xs 分别表示预期的失败和跳过的测试,圆点则表示测试通过。

    跳过的测试通常是因为缺少运行测试的外部库;请检查运行所有测试需要使用的依赖的列表,确保安装了所有与你正在修改的相关的测试测所有依赖(本教程不需要额外安装任何依赖)。一些特定的测试用于测试数据库后端,如果不做后端测试,那么就会被跳过。SQLite是默认的数据库后端。运行其他后端的测试,请参阅https://docs.djangoproject.com/en/2.0/internals/contributing/writing-code/unit-tests/

    一旦测试完成,你应该会收到一条消息,通知你测试套件通过还是未通过。由于你尚未对django的代码做任何更改,测试应该是通过的。如果遇到失败或者错误,请确保你按照前面步骤执行了所有操作。更多的信息请查看https://docs.djangoproject.com/en/2.0/internals/contributing/writing-code/unit-tests/。如果你使用的是Python 3.5+,你会遇到一些可以忽略的与弃用相关的失败内容,这些失败内容已经在django中进行了修复。

    注意,最新的django主干版本可能不是稳定版。当针对主干进行开发时,你可以先检查一下django的持续集成,以确定故障是否仅在你的机器上发生,或者是否他们也存在于django的官方构建中。如果点击了查看特定构建,则可以看到“Configuration Matrix”,其中显示了细分后的关于Python版本和数据库后端的故障信息。

    注意
    对于本教程和正在处理的故障问题,测试SQLite是足够了,但是还有可能使用不同的数据库后端进行测试(有时测试必须的)。

    为你的更新创建一个分支

    在做出更改前,先为你的故障创建一个分支:

    $ git checkout -b ticket_24788
    

    你可以随意为你的分支命名,“ticket_24788”仅仅是一个示例。在这个分支中,所有的更改都只针对前面遇到的故障,且不会影响到之前我们克隆的代码的主副本。

    为故障编写测试

    在大多数情况下,对于要接受的django补丁,必须包含测试用例。对于bug修复补丁来说,编写回归测试以确保这个bug以后不会再重新被引入django中。回归测试要写成的样子是 bug存在时会失败,bug修复后会通过。对于包含新功能的补丁,你需要包含保证新功能正常工作的测试。他们也应该在新功能不存在时失败,新功能实现后通过。

    实现这种操作的一种好方法是,在更改代码前先编写新的测试。这种开发方式成为 测试驱动开发,可以应用于整个项目或者是单个的补丁。在编写测试后,运行这些测试以确保他们执行失败(因为这里还没有修复bug或者添加新的功能)。如果新的测试执行时没有失败,那就需要对他们进行修复以便他们执行正确,毕竟,无论是否存在错误,都会通过回归测试来测试,这对防止错误的出现非常有帮助。
    下面的例子我们亲自动手实现。

    为工单 #24788 编写测试

    工单#24788提出了添加一个小功能:能够在Form类上指定类级别属性prefix。

    [...]与app一起提供的表单可以有效的命名自己,这样N个重叠的表单字段可以立即发布并解析为正确形式。

    为了解决这个工单,我们需要为BaseForm类添加一个prefix属性,当创建这个类的实例时,把一个前缀传递给__init__()方法,会把这个前缀设置到创建的实例上。但不传递前缀(或者传递的是None)则会使用类级浅灰。在我们做更改之前,我们会写几个测试验证我们的修改是正常的,且在未来也可以正常工作。

    进入Django的tests/forms_tests/tests/目录,打开test_forms.py文件。在test_forms_with_null_boolean函数之前的1674行加入以下代码:

    def test_class_prefix(self):
        # Prefix can be also specified at the class level.
        class Person(Form):
            first_name = CharField()
            prefix = 'foo'
    
        p = Person()
        self.assertEqual(p.prefix, 'foo')
    
        p = Person(prefix='bar')
        self.assertEqual(p.prefix, 'bar')
    

    这个测试用于检查设置的类级别前缀是否按预期工作,并且在创建实例时,传递prefix参数人就可用。

    这个测试看起来可能有些难
    如果你没有编写过测试,乍一看,感觉会有点难写。幸运的是,在计算机编程中,测试是一个非常大的概念,因此有很多相关的内容:

    • 为Django编写的测试的第一个好的介绍可以在编写和运行测试的文档中找到。
    • Dive Into Python(一个免费的在线书籍,适合初学python的开发者)包含了精彩的 单元测试介绍
    • 读完这些内容后,如果你还想了解更多的内容,那么还有Python的 unittest 文档。

    运行新的测试

    记住,我们还么有对BaseForm做任何修改,所有我们的测试会失败。我们在forms_tests目录中运行所有的测试,确保所有的测试都实际执行了。在命令行中,cd进Django的 tests/目录,并运行下面的命令:

    $ ./runtests.py forms_tests
    

    如果测试运行正确,你应该可以看到与我们添加的测试对应的一个失败。如果所有的测试都通过了,那么你需要确保将之前的新测试添加到相应的目录和文件中。

    为你的工单编写代码

    接下来,我们将把工单 #24788中描述的功能添加到Django。

    为工单 #24788 编写代码

    进入django/django/forms/目录,并打开forms.py文件。找到72上的 BaseForm类,在field_order属性之后添加prefix类属性。

    class BaseForm:
        # This is the main implementation of all the Form logic. Note that this
        # class is different than Form. See the comments by the Form class for
        # more information. Any improvements to the form API should be made to
        # *this* class, not to the Form class.
        field_order = None
        prefix = None
    

    验证测试通过

    当你完成了django的修改,就需要确保之前编写的测试通过,这样我们就可以看到上面编写的代码是否工作正常。在forms_tests目录中运行测试,cd到django的tests/目录,并运行下面的命令:

    $ ./runtests.py forms_tests
    

    糟糕,幸亏我们编写了测试。你应该可以看到类似下面异常的一个失败:

    AssertionError: None != 'foo'
    

    我们忘记了在 init 方法中添加条件判断语句。修改django/forms/forms.py中的87行为self.prefix = prefix,添加条件语句:

    if prefix is not None:
        self.prefix = prefix
    

    重新运行测试,所有的测试应该都会通过。如果没有,确保你像前面展示的一样正确的修改了BaseForm类,并正确复制了新测试。

    第二次运行Django的测试套件

    当你验证了你的补丁和测试可以正常工作后,最好运行django的整个测试组件来检查你做的更改是否把其他bug引入了django的其他区域。虽然整个测试组件通过测试不代表你的代码没有问题,但它有助于检查出许多被忽视的bug。

    cd进入Django的tests/目录,运行django的整个测试组件:

    $ ./runtests.py
    

    只要没有看到失败信息,就可以继续后面的内容了。

    编写文档

    这是一个新功能,因此应该记录到文档中,在django/docs/ref/forms/api.txt:文件中的1068行(文件的底部)添加下面的内容:

    The prefix can also be specified on the form class::
    
        >>> class PersonForm(forms.Form):
        ...     ...
        ...     prefix = 'person'
    
    .. versionadded:: 1.9
    
        The ability to specify ``prefix`` on the form class was added.
    

    由于这个新功能会出现在即将发布的新版本中,因为它会被添加到 Django1.9的发布说明中,位置在docs/releases/1.9.txt文件的164行,关于“Forms”的部分。

    • 可以在form类中指定表单前缀,而不仅仅是在实例化form时,更多信息请参阅:ref:form-prefix

    有关编写文档的更多信息,包括有关版本versionadded的说明,请参阅https://docs.djangoproject.com/en/2.0/internals/contributing/writing-documentation/。这个页面也包含如何构建文档的一个本地副本,以便你可以预览之后要生成的HTML。

    预览更改

    现在是时候完成补丁中所有的更改了。要心事当前django副本(包含你做的修改的)与本教程之前检出的修订版本之间的差异,执行下面的命令:

    $ git diff
    

    使用箭头键进行上下查看:

    diff --git a/django/forms/forms.py b/django/forms/forms.py
    index 509709f..d1370de 100644
    --- a/django/forms/forms.py
    +++ b/django/forms/forms.py
    @@ -75,6 +75,7 @@ class BaseForm:
         # information. Any improvements to the form API should be made to *this*
         # class, not to the Form class.
         field_order = None
    +    prefix = None
    
         def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
                      initial=None, error_class=ErrorList, label_suffix=None,
    @@ -83,7 +84,8 @@ class BaseForm:
             self.data = data or {}
             self.files = files or {}
             self.auto_id = auto_id
    -        self.prefix = prefix
    +        if prefix is not None:
    +            self.prefix = prefix
             self.initial = initial or {}
             self.error_class = error_class
             # Translators: This is the default suffix added to form field labels
    diff --git a/docs/ref/forms/api.txt b/docs/ref/forms/api.txt
    index 3bc39cd..008170d 100644
    --- a/docs/ref/forms/api.txt
    +++ b/docs/ref/forms/api.txt
    @@ -1065,3 +1065,13 @@ You can put several Django forms inside one ``<form>`` tag. To give each
         >>> print(father.as_ul())
         <li><label for="id_father-first_name">First name:</label> <input type="text" name="father-first_name" id="id_father-first_name" /></li>
         <li><label for="id_father-last_name">Last name:</label> <input type="text" name="father-last_name" id="id_father-last_name" /></li>
    +
    +The prefix can also be specified on the form class::
    +
    +    >>> class PersonForm(forms.Form):
    +    ...     ...
    +    ...     prefix = 'person'
    +
    +.. versionadded:: 1.9
    +
    +    The ability to specify ``prefix`` on the form class was added.
    diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt
    index 5b58f79..f9bb9de 100644
    --- a/docs/releases/1.9.txt
    +++ b/docs/releases/1.9.txt
    @@ -161,6 +161,9 @@ Forms
       :attr:`~django.forms.Form.field_order` attribute, the ``field_order``
       constructor argument , or the :meth:`~django.forms.Form.order_fields` method.
    
    +* A form prefix can be specified inside a form class, not only when
    +  instantiating a form. See :ref:`form-prefix` for details.
    +
     Generic Views
     ^^^^^^^^^^^^^
    
    diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py
    index 690f205..e07fae2 100644
    --- a/tests/forms_tests/tests/test_forms.py
    +++ b/tests/forms_tests/tests/test_forms.py
    @@ -1671,6 +1671,18 @@ class FormsTestCase(SimpleTestCase):
             self.assertEqual(p.cleaned_data['last_name'], 'Lennon')
             self.assertEqual(p.cleaned_data['birthday'], datetime.date(1940, 10, 9))
    
    +    def test_class_prefix(self):
    +        # Prefix can be also specified at the class level.
    +        class Person(Form):
    +            first_name = CharField()
    +            prefix = 'foo'
    +
    +        p = Person()
    +        self.assertEqual(p.prefix, 'foo')
    +
    +        p = Person(prefix='bar')
    +        self.assertEqual(p.prefix, 'bar')
    +
         def test_forms_with_null_boolean(self):
             # NullBooleanField is a bit of a special case because its presentation (widget)
             # is different than its data. This is handled transparently, though.
    

    完成预览修补后,按q键返回命令行。如果补丁的内容看起来没问题,那么是时候提交更改了。

    提交修改

    执行以下命令以提交修改:

    $ git commit -a
    

    执行该命令会打开一个文本编辑器以输入提交信息,按照信息提交规则写入类似于下面的信息:

    Fixed #24788 -- Allowed Forms to specify a prefix at the class level.
    

    推送提交并提交拉取请求

    提交补丁后,将其发送到github上的fork(如果不一样,可以使用你的分支名代替“ticket_24788):

    $ git push origin ticket_24788
    

    你可以通过访问Django GitHub页面来创建一个拉取请求。你会在“Your recently pushed branches”下看到你的分支,点击它旁边的“Compare & pull request” 。

    在本教程范围内,你不要执行该操作,但在下个页面会显示补丁的预览,你可以点击 “Create pull request”。

    下一步

    祝贺你,你已经学会了向Django发送pull请求。更多高级技巧的信息可以查看使用Git和GitHub工作

    现在你可以通过帮助django改进代码库来充分使用这些技巧。

    有关贡献者的贡多信息

    在你开始为django编写补丁前,还有一些关于贡献者的内容需要你了解一下:

    • 你应确保你已经阅读了django文档中认领工单和提交补丁的部分,。它涵盖了Trac规则(Trac是一个软件),如何认领工单、补丁的代码风格,和许多其他重要细节。
    • 第一次贡献代码的时候也应该阅读django中关于第一次贡献代码的文档。对于刚接触django的新人来说,它有许多很好的建议。
    • 之后,如果你希望了解更多关于贡献者的信息,你也可以查看django文档中关于贡献的其余部分。这里包含大量有用的信息,应该是解答你的问题的第一手资料。

    找出你的第一个工单

    当你查看了上面提到的内容后,就可以去找一个工单来编写补丁了。特别要注意哪些“easy pickings”工单。这些工单非常简单,非常适合初次参与的贡献者。当你熟悉了如何向Django提交贡献,你就可以改而解决更复杂困难的工单。

    如果你只是想了解一下,请查看需要打补丁的简单工单需要该进的补丁的工单。如果你熟悉编写测试,你可以查看需要测试的简单工单。请记得遵循关于认领工单的知道原则,这些工单是在django文档
    请记得认领工单和提交补丁中,关于遵循Django文档链接中提到的认领工单的指导原则。

    创建pull请求后做什么?

    工单有补丁后,需要通过通过第二组人来进行审查。提交pull请求后,通过将工单上的标志设置为 “has patch”、“doesn’t need tests”等,来更新故障单的元数据。以便其他人来查看。贡献不代表要从头开始编写补丁。查看现有的补丁也是一项很有用的贡献,详细信息可以查看工单分类

  • 相关阅读:
    机器学习之朴素贝叶斯
    机器学习之KNN
    格式化数字
    web.xml中load-on-startup的作用
    ExecutorService线程池
    201404转成 2014.04
    DBCP连接池配置示例
    java 写文本换行
    打印IP 来源
    MySQL分库分表的一些技巧
  • 原文地址:https://www.cnblogs.com/resn/p/10083297.html
Copyright © 2020-2023  润新知