2012-10-31 08:09:49 +08:00
|
|
|
===================================
|
|
|
|
Writing your first patch for Django
|
|
|
|
===================================
|
|
|
|
|
|
|
|
Introduction
|
|
|
|
============
|
|
|
|
|
|
|
|
Interested in giving back to the community a little? Maybe you've found a bug
|
|
|
|
in Django that you'd like to see fixed, or maybe there's a small feature you
|
|
|
|
want added.
|
|
|
|
|
|
|
|
Contributing back to Django itself is the best way to see your own concerns
|
|
|
|
addressed. This may seem daunting at first, but it's really pretty simple.
|
|
|
|
We'll walk you through the entire process, so you can learn by example.
|
|
|
|
|
|
|
|
Who's this tutorial for?
|
|
|
|
------------------------
|
|
|
|
|
2015-02-04 22:34:32 +08:00
|
|
|
.. seealso::
|
|
|
|
|
|
|
|
If you are looking for a reference on how to submit patches, see the
|
|
|
|
:doc:`/internals/contributing/writing-code/submitting-patches`
|
|
|
|
documentation.
|
|
|
|
|
2012-10-31 08:09:49 +08:00
|
|
|
For this tutorial, we expect that you have at least a basic understanding of
|
|
|
|
how Django works. This means you should be comfortable going through the
|
|
|
|
existing tutorials on :doc:`writing your first Django app</intro/tutorial01>`.
|
|
|
|
In addition, you should have a good understanding of Python itself. But if you
|
2013-12-31 01:15:49 +08:00
|
|
|
don't, `Dive Into Python`__ is a fantastic (and free) online book for
|
|
|
|
beginning Python programmers.
|
2012-10-31 08:09:49 +08:00
|
|
|
|
|
|
|
Those of you who are unfamiliar with version control systems and Trac will find
|
|
|
|
that this tutorial and its links include just enough information to get started.
|
|
|
|
However, you'll probably want to read some more about these different tools if
|
|
|
|
you plan on contributing to Django regularly.
|
|
|
|
|
|
|
|
For the most part though, this tutorial tries to explain as much as possible,
|
|
|
|
so that it can be of use to the widest audience.
|
|
|
|
|
|
|
|
.. admonition:: Where to get help:
|
|
|
|
|
|
|
|
If you're having trouble going through this tutorial, please post a message
|
2013-10-04 06:51:22 +08:00
|
|
|
to |django-developers| or drop by `#django-dev on irc.freenode.net`__ to
|
2012-10-31 08:09:49 +08:00
|
|
|
chat with other Django users who might be able to help.
|
|
|
|
|
2014-06-11 03:16:31 +08:00
|
|
|
__ http://www.diveintopython3.net/
|
2012-10-31 08:09:49 +08:00
|
|
|
__ irc://irc.freenode.net/django-dev
|
|
|
|
|
|
|
|
What does this tutorial cover?
|
|
|
|
------------------------------
|
|
|
|
|
|
|
|
We'll be walking you through contributing a patch to Django for the first time.
|
|
|
|
By the end of this tutorial, you should have a basic understanding of both the
|
|
|
|
tools and the processes involved. Specifically, we'll be covering the following:
|
|
|
|
|
|
|
|
* Installing Git.
|
|
|
|
* How to download a development copy of Django.
|
|
|
|
* Running Django's test suite.
|
|
|
|
* Writing a test for your patch.
|
|
|
|
* Writing the code for your patch.
|
|
|
|
* Testing your patch.
|
2017-01-27 19:34:07 +08:00
|
|
|
* Submitting a pull request.
|
2012-10-31 08:09:49 +08:00
|
|
|
* Where to look for more information.
|
|
|
|
|
|
|
|
Once you're done with the tutorial, you can look through the rest of
|
|
|
|
:doc:`Django's documentation on contributing</internals/contributing/index>`.
|
|
|
|
It contains lots of great information and is a must read for anyone who'd like
|
|
|
|
to become a regular contributor to Django. If you've got questions, it's
|
|
|
|
probably got the answers.
|
|
|
|
|
2015-06-09 05:56:29 +08:00
|
|
|
.. admonition:: Python 3 required!
|
|
|
|
|
2017-12-06 01:40:46 +08:00
|
|
|
The current version of Django doesn't support Python 2.7. Get Python 3 at
|
|
|
|
`Python's download page <https://www.python.org/downloads/>`_ or with your
|
|
|
|
operating system's package manager.
|
2015-06-09 05:56:29 +08:00
|
|
|
|
|
|
|
.. admonition:: For Windows users
|
|
|
|
|
|
|
|
When installing Python on Windows, make sure you check the option "Add
|
|
|
|
python.exe to Path", so that it is always available on the command line.
|
|
|
|
|
2015-04-18 00:13:48 +08:00
|
|
|
Code of Conduct
|
|
|
|
===============
|
|
|
|
|
|
|
|
As a contributor, you can help us keep the Django community open and inclusive.
|
|
|
|
Please read and follow our `Code of Conduct <https://www.djangoproject.com/conduct/>`_.
|
|
|
|
|
2012-10-31 08:09:49 +08:00
|
|
|
Installing Git
|
|
|
|
==============
|
|
|
|
|
|
|
|
For this tutorial, you'll need Git installed to download the current
|
|
|
|
development version of Django and to generate patch files for the changes you
|
|
|
|
make.
|
|
|
|
|
|
|
|
To check whether or not you have Git installed, enter ``git`` into the command
|
2015-06-09 05:56:29 +08:00
|
|
|
line. If you get messages saying that this command could not be found, you'll
|
|
|
|
have to download and install it, see `Git's download page`__.
|
|
|
|
|
2012-10-31 08:09:49 +08:00
|
|
|
If you're not that familiar with Git, you can always find out more about its
|
|
|
|
commands (once it's installed) by typing ``git help`` into the command line.
|
|
|
|
|
2017-05-20 23:51:21 +08:00
|
|
|
__ https://git-scm.com/download
|
2012-10-31 08:09:49 +08:00
|
|
|
|
|
|
|
Getting a copy of Django's development version
|
|
|
|
==============================================
|
|
|
|
|
|
|
|
The first step to contributing to Django is to get a copy of the source code.
|
2016-11-04 12:47:49 +08:00
|
|
|
First, `fork Django on GitHub <https://github.com/django/django/fork>`__. Then,
|
|
|
|
from the command line, use the ``cd`` command to navigate to the directory
|
|
|
|
where you'll want your local copy of Django to live.
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2015-06-05 08:06:03 +08:00
|
|
|
Download the Django source code repository using the following command:
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2018-01-21 01:38:48 +08:00
|
|
|
.. console::
|
2015-06-05 08:06:03 +08:00
|
|
|
|
2016-10-27 07:15:33 +08:00
|
|
|
$ git clone git@github.com:YourGitHubName/django.git
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2015-06-09 05:56:29 +08:00
|
|
|
Now that you have a local copy of Django, you can install it just like you would
|
|
|
|
install any package using ``pip``. The most convenient way to do so is by using
|
2018-09-19 17:41:22 +08:00
|
|
|
a *virtual environment*, which is a feature built into Python that allows you
|
|
|
|
to keep a separate directory of installed packages for each of your projects so
|
|
|
|
that they don't interfere with each other.
|
2015-06-09 05:56:29 +08:00
|
|
|
|
2018-09-19 17:41:22 +08:00
|
|
|
It's a good idea to keep all your virtual environments in one place, for
|
|
|
|
example in ``.virtualenvs/`` in your home directory.
|
2015-06-09 05:56:29 +08:00
|
|
|
|
2018-09-19 17:41:22 +08:00
|
|
|
Create a new virtual environment by running:
|
2015-06-09 05:56:29 +08:00
|
|
|
|
2018-01-21 01:38:48 +08:00
|
|
|
.. console::
|
2015-06-09 05:56:29 +08:00
|
|
|
|
2018-09-19 17:41:22 +08:00
|
|
|
$ python3 -m venv ~/.virtualenvs/djangodev
|
2015-06-09 05:56:29 +08:00
|
|
|
|
|
|
|
The path is where the new environment will be saved on your computer.
|
|
|
|
|
2018-09-19 17:41:22 +08:00
|
|
|
The final step in setting up your virtual environment is to activate it:
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2015-06-09 05:56:29 +08:00
|
|
|
.. code-block:: console
|
|
|
|
|
|
|
|
$ source ~/.virtualenvs/djangodev/bin/activate
|
|
|
|
|
|
|
|
If the ``source`` command is not available, you can try using a dot instead:
|
|
|
|
|
|
|
|
.. code-block:: console
|
|
|
|
|
|
|
|
$ . ~/.virtualenvs/djangodev/bin/activate
|
|
|
|
|
|
|
|
.. admonition:: For Windows users
|
|
|
|
|
2018-09-19 17:41:22 +08:00
|
|
|
To activate your virtual environment on Windows, run:
|
2015-06-09 05:56:29 +08:00
|
|
|
|
2018-01-21 01:38:48 +08:00
|
|
|
.. code-block:: doscon
|
2015-06-09 05:56:29 +08:00
|
|
|
|
2018-01-21 01:38:48 +08:00
|
|
|
...\> %HOMEPATH%\.virtualenvs\djangodev\Scripts\activate.bat
|
2015-06-09 05:56:29 +08:00
|
|
|
|
2018-09-19 17:41:22 +08:00
|
|
|
You have to activate the virtual environment whenever you open a new
|
|
|
|
terminal window. virtualenvwrapper__ is a useful tool for making this
|
|
|
|
more convenient.
|
2015-06-09 05:56:29 +08:00
|
|
|
|
2016-04-28 22:09:57 +08:00
|
|
|
__ https://virtualenvwrapper.readthedocs.io/en/latest/
|
2015-06-09 05:56:29 +08:00
|
|
|
|
2018-09-19 17:41:22 +08:00
|
|
|
The name of the currently activated virtual environment is displayed on the
|
|
|
|
command line to help you keep track of which one you are using. Anything you
|
|
|
|
install through ``pip`` while this name is displayed will be installed in that
|
|
|
|
virtual environment, isolated from other environments and system-wide packages.
|
|
|
|
Go ahead and install the previously cloned copy of Django:
|
2015-06-09 05:56:29 +08:00
|
|
|
|
2018-01-21 01:38:48 +08:00
|
|
|
.. console::
|
2015-06-09 05:56:29 +08:00
|
|
|
|
|
|
|
$ pip install -e /path/to/your/local/clone/django/
|
|
|
|
|
|
|
|
The installed version of Django is now pointing at your local copy. You will
|
|
|
|
immediately see any changes you make to it, which is of great help when writing
|
|
|
|
your first patch.
|
2012-10-31 08:09:49 +08:00
|
|
|
|
|
|
|
Running Django's test suite for the first time
|
|
|
|
==============================================
|
|
|
|
|
|
|
|
When contributing to Django it's very important that your code changes don't
|
2015-06-05 08:06:03 +08:00
|
|
|
introduce bugs into other areas of Django. One way to check that Django still
|
2012-10-31 08:09:49 +08:00
|
|
|
works after you make your changes is by running Django's test suite. If all
|
|
|
|
the tests still pass, then you can be reasonably sure that your changes
|
|
|
|
haven't completely broken Django. If you've never run Django's test suite
|
|
|
|
before, it's a good idea to run it once beforehand just to get familiar with
|
|
|
|
what its output is supposed to look like.
|
|
|
|
|
2015-06-05 08:06:03 +08:00
|
|
|
Before running the test suite, install its dependencies by first ``cd``-ing
|
|
|
|
into the Django ``tests/`` directory and then running:
|
|
|
|
|
2018-01-21 01:38:48 +08:00
|
|
|
.. console::
|
2015-06-05 08:06:03 +08:00
|
|
|
|
2015-06-09 05:56:29 +08:00
|
|
|
$ pip install -r requirements/py3.txt
|
2015-06-05 08:06:03 +08:00
|
|
|
|
2016-07-30 08:03:48 +08:00
|
|
|
If you encounter an error during the installation, your system might be missing
|
|
|
|
a dependency for one or more of the Python packages. Consult the failing
|
|
|
|
package's documentation or search the Web with the error message that you
|
|
|
|
encounter.
|
|
|
|
|
2017-04-24 04:06:12 +08:00
|
|
|
Now we are ready to run the test suite. If you're using GNU/Linux, macOS, or
|
2015-06-05 08:06:03 +08:00
|
|
|
some other flavor of Unix, run:
|
|
|
|
|
2018-01-21 01:38:48 +08:00
|
|
|
.. console::
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2015-06-09 05:56:29 +08:00
|
|
|
$ ./runtests.py
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2018-09-02 03:27:43 +08:00
|
|
|
Now sit back and relax. Django's entire test suite has thousands of tests, and
|
|
|
|
it takes at least a few minutes run, depending on the speed of your computer.
|
2012-10-31 08:09:49 +08:00
|
|
|
|
|
|
|
While Django's test suite is running, you'll see a stream of characters
|
|
|
|
representing the status of each test as it's run. ``E`` indicates that an error
|
|
|
|
was raised during a test, and ``F`` indicates that a test's assertions failed.
|
|
|
|
Both of these are considered to be test failures. Meanwhile, ``x`` and ``s``
|
|
|
|
indicated expected failures and skipped tests, respectively. Dots indicate
|
|
|
|
passing tests.
|
|
|
|
|
|
|
|
Skipped tests are typically due to missing external libraries required to run
|
|
|
|
the test; see :ref:`running-unit-tests-dependencies` for a list of dependencies
|
|
|
|
and be sure to install any for tests related to the changes you are making (we
|
2016-04-06 03:52:34 +08:00
|
|
|
won't need any for this tutorial). Some tests are specific to a particular
|
|
|
|
database backend and will be skipped if not testing with that backend. SQLite
|
|
|
|
is the database backend for the default settings. To run the tests using a
|
|
|
|
different backend, see :ref:`running-unit-tests-settings`.
|
2012-10-31 08:09:49 +08:00
|
|
|
|
|
|
|
Once the tests complete, you should be greeted with a message informing you
|
|
|
|
whether the test suite passed or failed. Since you haven't yet made any changes
|
|
|
|
to Django's code, the entire test suite **should** pass. If you get failures or
|
|
|
|
errors make sure you've followed all of the previous steps properly. See
|
2017-02-18 08:45:34 +08:00
|
|
|
:ref:`running-unit-tests` for more information. There will be a couple failures
|
|
|
|
related to deprecation warnings that you can ignore. These failures have since
|
|
|
|
been fixed in Django.
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2018-01-18 00:42:35 +08:00
|
|
|
Note that the latest Django master may not always be stable. When developing
|
|
|
|
against master, you can check `Django's continuous integration builds`__ to
|
2012-10-31 08:09:49 +08:00
|
|
|
determine if the failures are specific to your machine or if they are also
|
|
|
|
present in Django's official builds. If you click to view a particular build,
|
|
|
|
you can view the "Configuration Matrix" which shows failures broken down by
|
|
|
|
Python version and database backend.
|
|
|
|
|
2017-05-20 23:51:21 +08:00
|
|
|
__ https://djangoci.com
|
2012-10-31 08:09:49 +08:00
|
|
|
|
|
|
|
.. note::
|
|
|
|
|
|
|
|
For this tutorial and the ticket we're working on, testing against SQLite
|
|
|
|
is sufficient, however, it's possible (and sometimes necessary) to
|
|
|
|
:ref:`run the tests using a different database
|
|
|
|
<running-unit-tests-settings>`.
|
|
|
|
|
2018-09-02 03:27:43 +08:00
|
|
|
Rolling back to a previous revision of Django
|
|
|
|
=============================================
|
|
|
|
|
|
|
|
For this tutorial, we'll be using ticket :ticket:`24788` as a case study, so
|
|
|
|
we'll rewind Django's version history in git to before that ticket's patch was
|
|
|
|
applied. This will allow us to go through all of the steps involved in writing
|
|
|
|
that patch from scratch, including running Django's test suite.
|
|
|
|
|
|
|
|
**Keep in mind that while we'll be using an older revision of Django for this
|
|
|
|
tutorial, you should always use the current version of the master branch when
|
|
|
|
working on your own patch for a ticket!**
|
|
|
|
|
|
|
|
.. note::
|
|
|
|
|
|
|
|
The patch for this ticket was written by Paweł Marczewski, and it was
|
|
|
|
applied to Django as `commit 4df7e8483b2679fc1cba3410f08960bac6f51115`__.
|
|
|
|
Consequently, we'll be using the revision of Django just prior to that,
|
|
|
|
`commit 4ccfc4439a7add24f8db4ef3960d02ef8ae09887`__.
|
|
|
|
|
|
|
|
__ https://github.com/django/django/commit/4df7e8483b2679fc1cba3410f08960bac6f51115
|
|
|
|
__ https://github.com/django/django/commit/4ccfc4439a7add24f8db4ef3960d02ef8ae09887
|
|
|
|
|
|
|
|
Navigate into Django's root directory (that's the one that contains ``django``,
|
|
|
|
``docs``, ``tests``, ``AUTHORS``, etc.). You can then check out the older
|
|
|
|
revision of Django that we'll be using in the tutorial below:
|
|
|
|
|
|
|
|
.. console::
|
|
|
|
|
|
|
|
$ git checkout 4ccfc4439a7add24f8db4ef3960d02ef8ae09887
|
|
|
|
|
2017-01-27 19:34:07 +08:00
|
|
|
Creating a branch for your patch
|
|
|
|
================================
|
|
|
|
|
|
|
|
Before making any changes, create a new branch for the ticket:
|
|
|
|
|
2018-01-21 01:38:48 +08:00
|
|
|
.. console::
|
2017-01-27 19:34:07 +08:00
|
|
|
|
|
|
|
$ git checkout -b ticket_24788
|
|
|
|
|
|
|
|
You can choose any name that you want for the branch, "ticket_24788" is an
|
|
|
|
example. All changes made in this branch will be specific to the ticket and
|
|
|
|
won't affect the main copy of the code that we cloned earlier.
|
|
|
|
|
2012-10-31 08:09:49 +08:00
|
|
|
Writing some tests for your ticket
|
|
|
|
==================================
|
|
|
|
|
|
|
|
In most cases, for a patch to be accepted into Django it has to include tests.
|
|
|
|
For bug fix patches, this means writing a regression test to ensure that the
|
|
|
|
bug is never reintroduced into Django later on. A regression test should be
|
|
|
|
written in such a way that it will fail while the bug still exists and pass
|
|
|
|
once the bug has been fixed. For patches containing new features, you'll need
|
|
|
|
to include tests which ensure that the new features are working correctly.
|
|
|
|
They too should fail when the new feature is not present, and then pass once it
|
|
|
|
has been implemented.
|
|
|
|
|
|
|
|
A good way to do this is to write your new tests first, before making any
|
|
|
|
changes to the code. This style of development is called
|
|
|
|
`test-driven development`__ and can be applied to both entire projects and
|
|
|
|
single patches. After writing your tests, you then run them to make sure that
|
|
|
|
they do indeed fail (since you haven't fixed that bug or added that feature
|
|
|
|
yet). If your new tests don't fail, you'll need to fix them so that they do.
|
|
|
|
After all, a regression test that passes regardless of whether a bug is present
|
|
|
|
is not very helpful at preventing that bug from reoccurring down the road.
|
|
|
|
|
|
|
|
Now for our hands-on example.
|
|
|
|
|
2015-08-08 18:02:32 +08:00
|
|
|
__ https://en.wikipedia.org/wiki/Test-driven_development
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2015-06-05 08:06:03 +08:00
|
|
|
Writing some tests for ticket #24788
|
2012-10-31 08:09:49 +08:00
|
|
|
------------------------------------
|
|
|
|
|
2015-06-05 08:06:03 +08:00
|
|
|
Ticket :ticket:`24788` proposes a small feature addition: the ability to
|
|
|
|
specify the class level attribute ``prefix`` on Form classes, so that::
|
|
|
|
|
|
|
|
[…] 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.
|
|
|
|
|
|
|
|
In order to resolve this ticket, we'll add a ``prefix`` attribute to the
|
|
|
|
``BaseForm`` class. When creating instances of this class, passing a prefix to
|
|
|
|
the ``__init__()`` method will still set that prefix on the created instance.
|
|
|
|
But not passing a prefix (or passing ``None``) will use the class-level prefix.
|
|
|
|
Before we make those changes though, we're going to write a couple tests to
|
|
|
|
verify that our modification functions correctly and continues to function
|
|
|
|
correctly in the future.
|
|
|
|
|
|
|
|
Navigate to Django's ``tests/forms_tests/tests/`` folder and open the
|
|
|
|
``test_forms.py`` file. Add the following code on line 1674 right before the
|
|
|
|
``test_forms_with_null_boolean`` function::
|
|
|
|
|
|
|
|
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')
|
|
|
|
|
|
|
|
This new test checks that setting a class level prefix works as expected, and
|
|
|
|
that passing a ``prefix`` parameter when creating an instance still works too.
|
2012-10-31 08:09:49 +08:00
|
|
|
|
|
|
|
.. admonition:: But this testing thing looks kinda hard...
|
|
|
|
|
|
|
|
If you've never had to deal with tests before, they can look a little hard
|
|
|
|
to write at first glance. Fortunately, testing is a *very* big subject in
|
|
|
|
computer programming, so there's lots of information out there:
|
|
|
|
|
|
|
|
* A good first look at writing tests for Django can be found in the
|
2013-12-31 19:24:11 +08:00
|
|
|
documentation on :doc:`/topics/testing/overview`.
|
2012-10-31 08:09:49 +08:00
|
|
|
* Dive Into Python (a free online book for beginning Python developers)
|
|
|
|
includes a great `introduction to Unit Testing`__.
|
|
|
|
* After reading those, if you want something a little meatier to sink
|
2016-05-09 06:07:43 +08:00
|
|
|
your teeth into, there's always the Python :mod:`unittest` documentation.
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2017-05-20 21:32:35 +08:00
|
|
|
__ http://www.diveintopython3.net/unit-testing.html
|
2012-10-31 08:09:49 +08:00
|
|
|
|
|
|
|
Running your new test
|
|
|
|
---------------------
|
|
|
|
|
2015-06-05 08:06:03 +08:00
|
|
|
Remember that we haven't actually made any modifications to ``BaseForm`` yet,
|
|
|
|
so our tests are going to fail. Let's run all the tests in the ``forms_tests``
|
|
|
|
folder to make sure that's really what happens. From the command line, ``cd``
|
|
|
|
into the Django ``tests/`` directory and run:
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2018-01-21 01:38:48 +08:00
|
|
|
.. console::
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2015-06-09 05:56:29 +08:00
|
|
|
$ ./runtests.py forms_tests
|
2015-06-05 08:06:03 +08:00
|
|
|
|
|
|
|
If the tests ran correctly, you should see one failure corresponding to the test
|
|
|
|
method we added. If all of the tests passed, then you'll want to make sure that
|
|
|
|
you added the new test shown above to the appropriate folder and class.
|
2012-10-31 08:09:49 +08:00
|
|
|
|
|
|
|
Writing the code for your ticket
|
|
|
|
================================
|
|
|
|
|
2015-06-05 08:06:03 +08:00
|
|
|
Next we'll be adding the functionality described in ticket :ticket:`24788` to
|
2014-12-19 22:54:29 +08:00
|
|
|
Django.
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2015-06-05 08:06:03 +08:00
|
|
|
Writing the code for ticket #24788
|
2012-10-31 08:09:49 +08:00
|
|
|
----------------------------------
|
|
|
|
|
2015-06-05 08:06:03 +08:00
|
|
|
Navigate to the ``django/django/forms/`` folder and open the ``forms.py`` file.
|
|
|
|
Find the ``BaseForm`` class on line 72 and add the ``prefix`` class attribute
|
|
|
|
right after the ``field_order`` attribute::
|
|
|
|
|
2017-06-26 22:30:31 +08:00
|
|
|
class BaseForm:
|
2015-06-05 08:06:03 +08:00
|
|
|
# This is the main implementation of all the Form logic. Note that this
|
2015-06-09 05:56:29 +08:00
|
|
|
# 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.
|
2015-06-05 08:06:03 +08:00
|
|
|
field_order = None
|
|
|
|
prefix = None
|
2012-10-31 08:09:49 +08:00
|
|
|
|
|
|
|
Verifying your test now passes
|
|
|
|
------------------------------
|
|
|
|
|
|
|
|
Once you're done modifying Django, we need to make sure that the tests we wrote
|
|
|
|
earlier pass, so we can see whether the code we wrote above is working
|
2015-06-05 08:06:03 +08:00
|
|
|
correctly. To run the tests in the ``forms_tests`` folder, ``cd`` into the
|
|
|
|
Django ``tests/`` directory and run:
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2018-01-21 01:38:48 +08:00
|
|
|
.. console::
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2015-06-09 05:56:29 +08:00
|
|
|
$ ./runtests.py forms_tests
|
2015-06-05 08:06:03 +08:00
|
|
|
|
|
|
|
Oops, good thing we wrote those tests! You should still see one failure with
|
2012-10-31 08:09:49 +08:00
|
|
|
the following exception::
|
|
|
|
|
2015-06-05 08:06:03 +08:00
|
|
|
AssertionError: None != 'foo'
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2015-06-05 08:06:03 +08:00
|
|
|
We forgot to add the conditional statement in the ``__init__`` method. Go ahead
|
|
|
|
and change ``self.prefix = prefix`` that is now on line 87 of
|
|
|
|
``django/forms/forms.py``, adding a conditional statement::
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2015-06-05 08:06:03 +08:00
|
|
|
if prefix is not None:
|
|
|
|
self.prefix = prefix
|
2012-10-31 08:09:49 +08:00
|
|
|
|
|
|
|
Re-run the tests and everything should pass. If it doesn't, make sure you
|
2015-06-05 08:06:03 +08:00
|
|
|
correctly modified the ``BaseForm`` class as shown above and copied the new test
|
|
|
|
correctly.
|
2012-10-31 08:09:49 +08:00
|
|
|
|
|
|
|
Running Django's test suite for the second time
|
|
|
|
===============================================
|
|
|
|
|
|
|
|
Once you've verified that your patch and your test are working correctly, it's
|
|
|
|
a good idea to run the entire Django test suite just to verify that your change
|
|
|
|
hasn't introduced any bugs into other areas of Django. While successfully
|
|
|
|
passing the entire test suite doesn't guarantee your code is bug free, it does
|
|
|
|
help identify many bugs and regressions that might otherwise go unnoticed.
|
|
|
|
|
|
|
|
To run the entire Django test suite, ``cd`` into the Django ``tests/``
|
2015-06-05 08:06:03 +08:00
|
|
|
directory and run:
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2018-01-21 01:38:48 +08:00
|
|
|
.. console::
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2015-06-09 05:56:29 +08:00
|
|
|
$ ./runtests.py
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2018-09-02 03:27:43 +08:00
|
|
|
Remember that for this tutorial you're working from an older version of Django.
|
|
|
|
You may see a few unrelated failures that have since been fixed in Django's
|
|
|
|
master branch.
|
2012-10-31 08:09:49 +08:00
|
|
|
|
|
|
|
Writing Documentation
|
|
|
|
=====================
|
|
|
|
|
2015-06-05 08:06:03 +08:00
|
|
|
This is a new feature, so it should be documented. Add the following section on
|
|
|
|
line 1068 (at the end of the file) of ``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.
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2015-06-05 08:06:03 +08:00
|
|
|
Since this new feature will be in an upcoming release it is also added to the
|
|
|
|
release notes for Django 1.9, on line 164 under the "Forms" section in the file
|
|
|
|
``docs/releases/1.9.txt``::
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2015-06-05 08:06:03 +08:00
|
|
|
* A form prefix can be specified inside a form class, not only when
|
|
|
|
instantiating a form. See :ref:`form-prefix` for details.
|
2012-10-31 08:09:49 +08:00
|
|
|
|
|
|
|
For more information on writing documentation, including an explanation of what
|
|
|
|
the ``versionadded`` bit is all about, see
|
|
|
|
:doc:`/internals/contributing/writing-documentation`. That page also includes
|
|
|
|
an explanation of how to build a copy of the documentation locally, so you can
|
|
|
|
preview the HTML that will be generated.
|
|
|
|
|
2017-01-27 19:34:07 +08:00
|
|
|
Previewing your changes
|
|
|
|
=======================
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2017-01-27 19:34:07 +08:00
|
|
|
Now it's time to go through all the changes made in our patch. To display the
|
|
|
|
differences between your current copy of Django (with your changes) and the
|
|
|
|
revision that you initially checked out earlier in the tutorial:
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2018-01-21 01:38:48 +08:00
|
|
|
.. console::
|
2015-06-05 08:06:03 +08:00
|
|
|
|
|
|
|
$ git diff
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2017-01-27 19:34:07 +08:00
|
|
|
Use the arrow keys to move up and down.
|
2012-10-31 08:09:49 +08:00
|
|
|
|
|
|
|
.. code-block:: diff
|
|
|
|
|
2015-06-05 08:06:03 +08:00
|
|
|
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
|
2017-06-26 22:30:31 +08:00
|
|
|
@@ -75,6 +75,7 @@ class BaseForm:
|
2015-06-05 08:06:03 +08:00
|
|
|
# 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,
|
2017-06-26 22:30:31 +08:00
|
|
|
@@ -83,7 +84,8 @@ class BaseForm:
|
2015-06-05 08:06:03 +08:00
|
|
|
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())
|
2018-01-21 15:09:10 +08:00
|
|
|
<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>
|
2015-06-05 08:06:03 +08:00
|
|
|
+
|
|
|
|
+The prefix can also be specified on the form class::
|
|
|
|
+
|
|
|
|
+ >>> class PersonForm(forms.Form):
|
|
|
|
+ ... ...
|
|
|
|
+ ... prefix = 'person'
|
2012-10-31 08:09:49 +08:00
|
|
|
+
|
2015-06-05 08:06:03 +08:00
|
|
|
+.. versionadded:: 1.9
|
2012-10-31 08:09:49 +08:00
|
|
|
+
|
2015-06-05 08:06:03 +08:00
|
|
|
+ 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.
|
2012-10-31 08:09:49 +08:00
|
|
|
+
|
2015-06-05 08:06:03 +08:00
|
|
|
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'
|
2012-10-31 08:09:49 +08:00
|
|
|
+
|
2015-06-05 08:06:03 +08:00
|
|
|
+ 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.
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2017-01-27 19:34:07 +08:00
|
|
|
When you're done previewing the patch, hit the ``q`` key to return to the
|
|
|
|
command line. If the patch's content looked okay, it's time to commit the
|
|
|
|
changes.
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2017-01-27 19:34:07 +08:00
|
|
|
Committing the changes in the patch
|
|
|
|
===================================
|
|
|
|
|
|
|
|
To commit the changes:
|
|
|
|
|
2018-01-21 01:38:48 +08:00
|
|
|
.. console::
|
2017-01-27 19:34:07 +08:00
|
|
|
|
|
|
|
$ git commit -a
|
|
|
|
|
|
|
|
This opens up a text editor to type the commit message. Follow the :ref:`commit
|
|
|
|
message guidelines <committing-guidelines>` and write a message like:
|
|
|
|
|
|
|
|
.. code-block:: text
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2017-01-27 19:34:07 +08:00
|
|
|
Fixed #24788 -- Allowed Forms to specify a prefix at the class level.
|
|
|
|
|
|
|
|
Pushing the commit and making a pull request
|
|
|
|
============================================
|
|
|
|
|
|
|
|
After committing the patch, send it to your fork on GitHub (substitute
|
|
|
|
"ticket_24788" with the name of your branch if it's different):
|
2015-06-05 08:06:03 +08:00
|
|
|
|
2018-01-21 01:38:48 +08:00
|
|
|
.. console::
|
2012-10-31 08:09:49 +08:00
|
|
|
|
2017-01-27 19:34:07 +08:00
|
|
|
$ git push origin ticket_24788
|
|
|
|
|
|
|
|
You can create a pull request by visiting the `Django GitHub page
|
|
|
|
<https://github.com/django/django/>`_. You'll see your branch under "Your
|
|
|
|
recently pushed branches". Click "Compare & pull request" next to it.
|
|
|
|
|
2017-02-17 00:33:07 +08:00
|
|
|
Please don't do it for this tutorial, but on the next page that displays a
|
|
|
|
preview of the patch, you would click "Create pull request".
|
2017-01-27 19:34:07 +08:00
|
|
|
|
|
|
|
Next steps
|
|
|
|
==========
|
|
|
|
|
|
|
|
Congratulations, you've learned how to make a pull request to Django! Details
|
|
|
|
of more advanced techniques you may need are in
|
|
|
|
:doc:`/internals/contributing/writing-code/working-with-git`.
|
|
|
|
|
|
|
|
Now you can put those skills to good use by helping to improve Django's
|
|
|
|
codebase.
|
2012-10-31 08:09:49 +08:00
|
|
|
|
|
|
|
More information for new contributors
|
|
|
|
-------------------------------------
|
|
|
|
|
|
|
|
Before you get too into writing patches for Django, there's a little more
|
|
|
|
information on contributing that you should probably take a look at:
|
|
|
|
|
|
|
|
* You should make sure to read Django's documentation on
|
|
|
|
:doc:`claiming tickets and submitting patches
|
|
|
|
</internals/contributing/writing-code/submitting-patches>`.
|
|
|
|
It covers Trac etiquette, how to claim tickets for yourself, expected
|
|
|
|
coding style for patches, and many other important details.
|
|
|
|
* First time contributors should also read Django's :doc:`documentation
|
|
|
|
for first time contributors</internals/contributing/new-contributors/>`.
|
|
|
|
It has lots of good advice for those of us who are new to helping out
|
|
|
|
with Django.
|
|
|
|
* After those, if you're still hungry for more information about
|
|
|
|
contributing, you can always browse through the rest of
|
|
|
|
:doc:`Django's documentation on contributing</internals/contributing/index>`.
|
|
|
|
It contains a ton of useful information and should be your first source
|
|
|
|
for answering any questions you might have.
|
|
|
|
|
|
|
|
Finding your first real ticket
|
|
|
|
------------------------------
|
|
|
|
|
|
|
|
Once you've looked through some of that information, you'll be ready to go out
|
|
|
|
and find a ticket of your own to write a patch for. Pay special attention to
|
|
|
|
tickets with the "easy pickings" criterion. These tickets are often much
|
2015-06-05 08:06:03 +08:00
|
|
|
simpler in nature and are great for first time contributors. Once you're
|
2012-10-31 08:09:49 +08:00
|
|
|
familiar with contributing to Django, you can move on to writing patches for
|
|
|
|
more difficult and complicated tickets.
|
|
|
|
|
|
|
|
If you just want to get started already (and nobody would blame you!), try
|
|
|
|
taking a look at the list of `easy tickets that need patches`__ and the
|
|
|
|
`easy tickets that have patches which need improvement`__. If you're familiar
|
|
|
|
with writing tests, you can also look at the list of
|
|
|
|
`easy tickets that need tests`__. Just remember to follow the guidelines about
|
|
|
|
claiming tickets that were mentioned in the link to Django's documentation on
|
|
|
|
:doc:`claiming tickets and submitting patches
|
|
|
|
</internals/contributing/writing-code/submitting-patches>`.
|
|
|
|
|
|
|
|
__ https://code.djangoproject.com/query?status=new&status=reopened&has_patch=0&easy=1&col=id&col=summary&col=status&col=owner&col=type&col=milestone&order=priority
|
|
|
|
__ https://code.djangoproject.com/query?status=new&status=reopened&needs_better_patch=1&easy=1&col=id&col=summary&col=status&col=owner&col=type&col=milestone&order=priority
|
|
|
|
__ https://code.djangoproject.com/query?status=new&status=reopened&needs_tests=1&easy=1&col=id&col=summary&col=status&col=owner&col=type&col=milestone&order=priority
|
|
|
|
|
2017-01-27 19:34:07 +08:00
|
|
|
What's next after creating a pull request?
|
|
|
|
------------------------------------------
|
2012-10-31 08:09:49 +08:00
|
|
|
|
|
|
|
After a ticket has a patch, it needs to be reviewed by a second set of eyes.
|
2017-01-27 19:34:07 +08:00
|
|
|
After submitting a pull request, update the ticket metadata by setting the
|
|
|
|
flags on the ticket to say "has patch", "doesn't need tests", etc, so others
|
|
|
|
can find it for review. Contributing doesn't necessarily always mean writing a
|
|
|
|
patch from scratch. Reviewing existing patches is also a very helpful
|
|
|
|
contribution. See :doc:`/internals/contributing/triaging-tickets` for details.
|