Написание вашего первого патча для Django
Введение
Хотите внести свой вклад в сообщество? Возможно, вы обнаружили ошибку в Django, которую хотелось бы исправить, или хотите добавить небольшую функцию.
Внесение изменений в сам Django — лучший способ увидеть решение своих проблем. Это может показаться сложным вначале, но на самом деле это довольно просто. Мы пройдем вас через весь процесс, чтобы вы могли учиться на примерах.
Для кого этот учебник?
См. также
Если вы ищете справочник по отправке патчей, обратитесь к документации Отправка патчей.
Для этого учебника мы предполагаем, что вы имеете хотя бы базовое понимание того, как работает Django. Это означает, что вы должны чувствовать себя комфортно, проходя существующие учебники по созданию вашего первого приложения Django. Кроме того, вы должны хорошо понимать сам Python. Но если нет, Dive Into Python — отличная (и бесплатная) онлайн-книга для начинающих программистов Python.
Те из вас, кто не знаком с системами контроля версий и Trac, обнаружат, что этот учебник и его ссылки содержат достаточно информации для начала работы. Однако, если вы планируете регулярно участвовать в разработке Django, вам, вероятно, захочется прочитать больше о этих инструментах.
В основном, этот учебник пытается объяснить как можно больше, чтобы он был полезен для самых широких аудиторий.
Где получить помощь:
Если у вас возникнут трудности с прохождением этого учебника, отправьте сообщение на django-developers или зайдите на #django-dev на irc.freenode.net, чтобы пообщаться с другими пользователями Django, которые могут помочь.
Что охватывает этот учебник?
Мы пройдем вас через вклад патча в Django в первый раз. По завершении этого учебника вы должны иметь базовое представление об используемых инструментах и процессах. В частности, мы будем рассматривать следующие моменты:
- Установка Git.
- Как загрузить рабочую копию Django.
- Запуск набора тестов Django.
- Написание теста для вашего патча.
- Написание кода для вашего патча.
- Тестирование вашего патча.
- Отправка запроса на включение.
- Где найти дополнительную информацию.
После завершения учебника, вы можете ознакомиться с остальной частью документации Django по участию. Она содержит много полезной информации и является обязательной для прочтения для всех, кто хотел бы стать постоянным участником в развитии Django. Если у вас есть вопросы, ответы на них, скорее всего, там.
Требуется Python 3!
В этом руководстве предполагается, что вы используете Python 3. Скачайте последнюю версию на странице загрузки Python или с помощью менеджера пакетов вашей операционной системы.
Для пользователей Windows
При установке Python на Windows убедитесь, что вы выбрали опцию «Добавить python.exe в PATH», чтобы он всегда был доступен в командной строке.
Кодекс поведения
Как участник, вы можете помочь нам поддерживать открытость и инклюзивность сообщества Django. Пожалуйста, прочтите и следуйте нашему Кодексу поведения.
Установка Git
Для этого учебника вам понадобится Git, чтобы загрузить текущую версию разработки Django и создать файлы патчей для внесенных изменений.
Чтобы проверить установку Git, введите git в командной строке. Если вы получите сообщения о том, что эта команда не найдена, вам нужно будет загрузить и установить ее, см. страницу загрузки Git.
Для пользователей Windows
При установке Git на Windows рекомендуется выбрать опцию «Git Bash», чтобы Git запускался в собственном оболочке. Этот учебник предполагает, что вы установили его таким образом.
Если вы не очень хорошо знакомы с Git, вы всегда можете узнать больше о его командах (после установки), набрав git help в командной строке.
Получение копии версии разработки Django
Первый шаг к участию в Django — получить копию исходного кода. Сначала создайте форк Django на GitHub. Затем в командной строке используйте команду cd для перехода в каталог, где вы хотите разместить вашу локальную копию Django.
Загрузите репозиторий исходного кода Django с помощью следующей команды:
$ git clone git@github.com:YourGitHubName/django.git
Теперь, когда у вас есть локальная копия Django, вы можете установить ее так же, как любой пакет, используя pip. Наиболее удобный способ сделать это — использовать виртуальную среду (или virtualenv) — функцию, встроенную в Python, которая позволяет вам хранить отдельный каталог установленных пакетов для каждого вашего проекта, чтобы они не мешали друг другу.
Рекомендуется хранить все ваши виртуальные среды в одном месте, например, в .virtualenvs/ в вашем домашнем каталоге. Создайте его, если он еще не существует:
$ mkdir ~/.virtualenvs
Теперь создайте новую виртуальную среду, выполнив:
$ python3 -m venv ~/.virtualenvs/djangodev
Путь указывает место, где новая среда будет сохранена на вашем компьютере.
Для пользователей Windows
Использование встроенного модуля venv не сработает, если вы также используете оболочку Git Bash на Windows, так как скрипты активации создаются только для системной оболочки (.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
Последний шаг в настройке вашей виртуальной среды — активировать ее:
$ source ~/.virtualenvs/djangodev/bin/activate
Если команда source недоступна, вы можете попробовать использовать точку вместо неё:
$ . ~/.virtualenvs/djangodev/bin/activate
Для пользователей Windows
Чтобы активировать свою виртуальную среду на Windows, выполните:
$ source ~/virtualenvs/djangodev/Scripts/activate
Вам необходимо активировать виртуальную среду каждый раз при открытии нового окна терминала. virtualenvwrapper — полезный инструмент для упрощения этого процесса.
Все, что вы установите через pip с этого момента, будет установлено в вашей новой виртуальной среде, изолированно от других сред и системных пакетов. Также имя текущей активированной виртуальной среды отображается в командной строке, чтобы помочь вам отслеживать, какую из них вы используете. Установите ранее клонированную копию Django:
$ pip install -e /path/to/your/local/clone/django/
Теперь установленная версия Django указывает на вашу локальную копию. Вы сразу увидите любые изменения, которые вы внесете, что очень полезно при написании вашего первого патча.
Откат к предыдущей версии Django
В этом учебнике мы будем использовать тикет #24788 в качестве примера, поэтому мы вернем историю версий Django в git до применения патча этого тикета. Это позволит нам пройти все этапы написания этого патча с нуля, включая запуск набора тестов Django.
Обратите внимание, что, хотя мы будем использовать более старую версию trunk Django для целей данного учебника, вы всегда должны использовать текущую версию разработки Django при работе над своим собственным патчем для тикета!
Примечание
Патч для этого тикета был написан Павлом Марчевским и был применён к Django как commit 4df7e8483b2679fc1cba3410f08960bac6f51115. Следовательно, мы будем использовать версию Django непосредственно перед этим — commit 4ccfc4439a7add24f8db4ef3960d02ef8ae09887.
Перейдите в корневой каталог Django (тот, который содержит django, docs, tests, AUTHORS, и т. д.). Затем вы можете проверить более старую версию Django, которую мы будем использовать в учебнике ниже:
$ git checkout 4ccfc4439a7add24f8db4ef3960d02ef8ae09887
Запуск набора тестов Django в первый раз
При внесении изменений в Django очень важно, чтобы ваши изменения не вносили ошибки в другие области Django. Один из способов проверить, что Django по-прежнему работает после внесения изменений, — это запустить набор тестов Django. Если все тесты пройдут, то можно с достаточной уверенностью утверждать, что ваши изменения не полностью сломали Django. Если вы никогда раньше не запускали набор тестов Django, рекомендуется запустить его один раз предварительно, чтобы ознакомиться с тем, как должен выглядеть его вывод.
Перед запуском набора тестов установите необходимые зависимости, сначала перейдя в каталог Django tests/ и затем выполнив:
$ pip install -r requirements/py3.txt
Если во время установки возникнет ошибка, возможно, на вашем компьютере отсутствует зависимость для одного или нескольких пакетов Python. Обратитесь к документации проблемного пакета или выполните поиск в Интернете с сообщением об ошибке, которое у вас возникло.
Теперь мы готовы запустить набор тестов. Если вы используете GNU/Linux, Mac OS X или другую разновидность Unix, выполните:
$ ./runtests.py
Теперь расслабьтесь и отдохните. Весь набор тестов Django состоит более чем из 9600 различных тестов, поэтому его выполнение может занять от 5 до 15 минут, в зависимости от скорости вашего компьютера.
При выполнении набора тестов Django вы увидите поток символов, представляющих статус каждого теста по мере его выполнения. E указывает, что во время тестирования возникла ошибка, а F указывает на то, что проверки теста завершились неудачно. Оба этих показателя считаются неудачами тестирования. В то же время, x и s обозначают ожидаемые ошибки и пропущенные тесты соответственно. Точки указывают на успешные тесты.
Пропущенные тесты, как правило, связаны с отсутствием внешних библиотек, необходимых для запуска теста; см. Запуск всех тестов для списка зависимостей и убедитесь, что установлены все необходимые для тестов, связанные с внесенными вами изменениями (нам не понадобятся никакие для этого учебника). Некоторые тесты специфичны для определенного бэкенда базы данных и будут пропущены, если тестирование не выполняется с этим бэкендом. SQLite — это бэкенд базы данных по умолчанию. Чтобы запустить тесты с использованием другого бэкенда, см. Использование другого файла настроек.
После завершения тестирования вам должно появиться сообщение, информирующее вас о том, пройден ли набор тестов или нет. Поскольку вы еще не внесли никаких изменений в код Django, весь набор тестов должен пройти успешно. Если у вас есть ошибки или неудачи, убедитесь, что вы правильно выполнили все предыдущие шаги. См. Запуск модульных тестов для получения дополнительной информации. Если вы используете Python 3.5+ будет несколько ошибок, связанных с предупреждениями о устаревании, которые можно пропустить. Эти ошибки с тех пор были исправлены в Django.
Обратите внимание, что последняя версия Django trunk может не всегда быть стабильной. При разработке на trunk, вы можете проверить непрерывную интеграцию Django, чтобы определить, являются ли ошибки специфичными для вашей машины или они также присутствуют в официальных сборках Django. Если вы нажмёте, чтобы просмотреть конкретную сборку, вы можете просмотреть «Матрицу конфигурации», которая показывает ошибки, разбитые по версиям Python и бэкендам баз данных.
Примечание
Для этого учебника и задачи, над которой мы работаем, тестирование против SQLite достаточно, однако, возможно (и иногда необходимо) запустить тесты с использованием другой базы данных.
Создание ветки для вашего патча
Прежде чем вносить какие-либо изменения, создайте новую ветку для задачи:
$ git checkout -b ticket_24788
Вы можете выбрать любое имя для ветки, «ticket_24788» — это пример. Все изменения, внесенные в эту ветку, будут специфичны для задачи и не повлияют на основной код, который мы клонировали ранее.
Написание некоторых тестов для вашей задачи
В большинстве случаев, для того, чтобы патч был принят в Django, он должен содержать тесты. Для патчей исправления ошибок это означает написание регрессионного теста, чтобы гарантировать, что ошибка никогда не будет повторно введена в Django позже. Регрессионный тест должен быть написан таким образом, чтобы он завершался неудачно, пока ошибка существует, и успешно проходил, когда ошибка была исправлена. Для патчей, содержащих новые функции, вам необходимо включить тесты, которые гарантируют, что новые функции работают правильно. Они также должны завершаться неудачно, когда новая функция отсутствует, а затем успешно проходить, когда она будет реализована.
Хороший способ сделать это — написать новые тесты сначала, прежде чем вносить какие-либо изменения в код. Такой стиль разработки называется test-driven development и может быть применён как к полным проектам, так и к отдельным патчам. После написания тестов вы запускаете их, чтобы убедиться, что они действительно завершаются неудачно (поскольку вы ещё не исправили ошибку или не добавили функцию). Если ваши новые тесты не завершаются неудачно, вам нужно их исправить, чтобы они завершались неудачно. Ведь регрессионный тест, который проходит независимо от того, существует ли ошибка, не очень полезен для предотвращения повторного появления этой ошибки в будущем.
Теперь для нашего практического примера.
Написание тестов для задачи #24788
Задача #24788 предлагает небольшое добавление функции: возможность указать атрибут уровня класса prefix для классов Form, так что:
[…] forms which ship with apps could effectively namespace themselves such that N overlapping form fields could be POSTed at once and resolved to the correct form.
Для решения этой задачи мы добавим атрибут prefix к классу BaseForm. При создании экземпляров этого класса передача префикса в метод __init__() всё ещё будет устанавливать этот префикс в созданном экземпляре. Но не передача префикса (или передача None) будет использовать префикс уровня класса. Но перед внесением этих изменений, мы напишем несколько тестов, чтобы проверить, что наши изменения работают правильно и продолжают работать правильно в будущем.
Перейдите в папку tests/forms_tests/tests/ Django и откройте файл test_forms.py. Добавьте следующий код в строку 1674, непосредственно перед функцией test_forms_with_null_boolean:
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 , чтобы убедиться, что это действительно произойдёт. Из командной строки перейдите в каталог Django tests/ и выполните:
$ ./runtests.py forms_tests
Если тесты прошли правильно, вы должны увидеть одну неудачу, соответствующую методу теста, который мы добавили. Если все тесты прошли, то вы хотите убедиться, что вы добавили новый тест, показанный выше, в соответствующую папку и класс.
Написание кода для вашей задачи
Далее мы добавим функциональность, описанную в задаче #24788, в Django.
Написание кода для задачи #24788
Перейдите в папку django/django/forms/ и откройте файл forms.py. Найдите класс BaseForm в строке 72 и добавьте атрибут prefix класса сразу после атрибута field_order:
class BaseForm(object):
# 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, перейдите в каталог Django tests/ и выполните:
$ ./runtests.py forms_tests
Опс, хорошо, что мы написали эти тесты! Вы всё ещё увидите одну неудачу с следующей ошибкой:
AssertionError: None != 'foo'
Мы забыли добавить условное выражение в метод __init__. Измените self.prefix = prefix, который сейчас находится на строке 87 файла django/forms/forms.py, добавив условное выражение:
if prefix is not None:
self.prefix = prefix
Запустите тесты снова, и всё должно пройти успешно. Если нет, убедитесь, что вы правильно изменили класс BaseForm так, как показано выше, и правильно скопировали новый тест.
Запуск набора тестов Django во второй раз
После того, как вы проверили, что ваш патч и ваш тест работают правильно, рекомендуется запустить весь набор тестов Django, чтобы убедиться, что ваше изменение не ввело никаких ошибок в другие области Django. Хотя успешное прохождение всего набора тестов не гарантирует отсутствие ошибок в вашем коде, это помогает выявить многие ошибки и регрессии, которые иначе могли бы остаться незамеченными.
Чтобы запустить весь набор тестов Django, перейдите в каталог Django tests/ и выполните:
$ ./runtests.py
Пока вы не видите никаких ошибок, всё в порядке.
Написание документации
Это новая функция, поэтому её следует задокументировать. Добавьте следующий раздел в строку 1068 (в конец файла) файла django/docs/ref/forms/api.txt:
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.
Поскольку эта новая функция будет включена в будущие версии, она также добавлена в заметки к выпуску Django 1.9 на строке 164 в разделе «Формы» файла docs/releases/1.9.txt:
* A form prefix can be specified inside a form class, not only when instantiating a form. See :ref:`form-prefix` for details.
Для получения дополнительной информации о написании документации, в том числе объяснения того, что означает versionadded , см. Написание документации. Эта страница также содержит объяснение того, как создать локальную копию документации, чтобы вы могли предварительно просмотреть сгенерированный 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(object):
# 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(object):
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 (замените «ticket_24788» именем вашей ветки, если она отличается):
$ git push origin ticket_24788
Вы можете создать запрос на вытягивание, перейдя на страницу GitHub Django. Ваша ветка будет отображаться в разделе «Ваши недавно отправленные ветки». Нажмите «Сравнить и создать запрос на вытягивание» рядом с ней.
Пожалуйста, не делайте этого для этого учебника, но на следующей странице, отображающей предварительный просмотр исправления, вы должны нажать «Создать запрос на вытягивание».
Следующие шаги
Поздравляем, вы узнали, как создать запрос на вытягивание для Django! Более подробные сведения о продвинутых методах, которые вам могут потребоваться, находятся в работе с Git и GitHub.
Теперь вы можете использовать эти навыки, чтобы помочь улучшить базу кода Django.
Дополнительная информация для новых участников
Прежде чем углубиться в написание исправлений для Django, есть немного больше информации о сотрудничестве, которую вы, вероятно, захотите изучить:
- Вы должны прочитать документацию Django по заявке на задания и отправке исправлений. Она охватывает этикет Trac, как заявить задания на себя, ожидаемый стиль кодирования для исправлений и многие другие важные детали.
- Первые участники также должны прочитать документацию Django для новых участников. Она содержит много полезных советов для тех из нас, кто только начинает помогать с Django.
- После этого, если вы все еще хотите узнать больше о сотрудничестве, вы всегда можете просмотреть остальную часть документации Django по сотрудничеству. Она содержит массу полезной информации и должна быть вашим основным источником для ответа на любые вопросы, которые у вас могут возникнуть.
Нахождение вашего первого реального задания
После ознакомления с этой информацией вы будете готовы выйти и найти задание, для которого напишите исправление. Обратите особое внимание на задания с критерием «легкие». Эти задания часто более простые по своей природе и идеально подходят для новых участников. После того, как вы освоите вклад в Django, вы можете перейти к написание исправлений для более сложных и запутанных заданий.
Если вы просто хотите начать работу (и никто не будет против!), попробуйте взглянуть на список легких заданий, нуждающихся в исправлениях и легких заданий, исправления которых нуждаются в улучшении. Если вы знакомы с написанием тестов, вы также можете посмотреть список легких заданий, которые нуждаются в тестах. Не забудьте следовать инструкциям о заявке на задания, упомянутым в ссылке на документацию Django по заявке на задания и отправке исправлений.
Что делать после создания запроса на вытягивание?
После того, как к заданию есть исправление, его необходимо проверить второй раз. После отправки запроса на вытягивание обновите метаданные задания, установив флажки на задании, чтобы указать, что «есть исправление», «не нужны тесты» и т. д., чтобы другие могли его найти для проверки. Содействие не обязательно всегда означает написание исправления с нуля. Проверка существующих исправлений также является очень полезным вкладом. См. классификацию заданий для получения подробностей.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.10/intro/contributing/