123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677 |
- ===================================
- 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?
- ------------------------
- .. seealso::
- If you are looking for a reference on how to submit patches, see the
- :doc:`/internals/contributing/writing-code/submitting-patches`
- documentation.
- 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
- don't, `Dive Into Python`__ is a fantastic (and free) online book for
- beginning Python programmers.
- 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
- to |django-developers| or drop by `#django-dev on irc.freenode.net`__ to
- chat with other Django users who might be able to help.
- __ http://www.diveintopython3.net/
- __ 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.
- * Generating a patch file for your changes.
- * 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.
- .. admonition:: Python 3 required!
- This tutorial assumes you are using Python 3. Get the latest version at
- `Python's download page <https://www.python.org/download/>`_ or with your
- operating system's package manager.
- .. 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.
- 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/>`_.
- 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
- 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`__.
- .. admonition:: For Windows users
- When installing Git on Windows, it is recommended that you pick the
- "Git Bash" option so that Git runs in its own shell. This tutorial assumes
- that's how you have installed it.
- 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.
- __ http://git-scm.com/download
- Getting a copy of Django's development version
- ==============================================
- The first step to contributing to Django is to get a copy of the source code.
- 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.
- Download the Django source code repository using the following command:
- .. code-block:: console
- $ git clone git@github.com:YourGitHubName/django.git
- 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
- a *virtual environment* (or virtualenv) 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.
- It's a good idea to keep all your virtualenvs in one place, for example in
- ``.virtualenvs/`` in your home directory. Create it if it doesn't exist yet:
- .. code-block:: console
- $ mkdir ~/.virtualenvs
- Now create a new virtualenv by running:
- .. code-block:: console
- $ python3 -m venv ~/.virtualenvs/djangodev
- The path is where the new environment will be saved on your computer.
- .. admonition:: For Windows users
- Using the built-in ``venv`` module will not work if you are also using the
- Git Bash shell on Windows, since activation scripts are only created for the
- system shell (``.bat``) and PowerShell (``.ps1``). Use the ``virtualenv``
- package instead:
- .. code-block:: none
- $ pip install virtualenv
- $ virtualenv ~/.virtualenvs/djangodev
- .. admonition:: For Ubuntu users
- On some versions of Ubuntu the above command might fail. Use the
- ``virtualenv`` package instead, first making sure you have ``pip3``:
- .. code-block:: console
- $ 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
- The final step in setting up your virtualenv is to activate it:
- .. 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
- To activate your virtualenv on Windows, run:
- .. code-block:: none
- $ source ~/virtualenvs/djangodev/Scripts/activate
- You have to activate the virtualenv whenever you open a new terminal window.
- virtualenvwrapper__ is a useful tool for making this more convenient.
- __ https://virtualenvwrapper.readthedocs.io/en/latest/
- Anything you install through ``pip`` from now on will be installed in your new
- virtualenv, isolated from other environments and system-wide packages. Also, the
- name of the currently activated virtualenv is displayed on the command line to
- help you keep track of which one you are using. Go ahead and install the
- previously cloned copy of Django:
- .. code-block:: console
- $ 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.
- 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's trunk
- for the purposes of the tutorial below, you should always use the current
- development revision of Django 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:
- .. code-block:: console
- $ git checkout 4ccfc4439a7add24f8db4ef3960d02ef8ae09887
- Running Django's test suite for the first time
- ==============================================
- When contributing to Django it's very important that your code changes don't
- introduce bugs into other areas of Django. One way to check that Django still
- 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.
- Before running the test suite, install its dependencies by first ``cd``-ing
- into the Django ``tests/`` directory and then running:
- .. code-block:: console
- $ pip install -r requirements/py3.txt
- 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.
- Now we are ready to run the test suite. If you're using GNU/Linux, Mac OS X or
- some other flavor of Unix, run:
- .. code-block:: console
- $ ./runtests.py
- Now sit back and relax. Django's entire test suite has over 9,600 different
- tests, so it can take anywhere from 5 to 15 minutes to run, depending on the
- speed of your computer.
- 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
- 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`.
- 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
- :ref:`running-unit-tests` for more information. If you're using Python 3.5+,
- there will be a couple failures related to deprecation warnings that you can
- ignore. These failures have since been fixed in Django.
- Note that the latest Django trunk may not always be stable. When developing
- against trunk, you can check `Django's continuous integration builds`__ to
- 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.
- __ http://djangoci.com
- .. 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>`.
- 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.
- __ https://en.wikipedia.org/wiki/Test-driven_development
- Writing some tests for ticket #24788
- ------------------------------------
- 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.
- .. 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
- documentation on :doc:`/topics/testing/overview`.
- * 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
- your teeth into, there's always the Python :mod:`unittest` documentation.
- __ http://www.diveintopython.net/unit_testing/index.html
- Running your new test
- ---------------------
- 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:
- .. code-block:: console
- $ ./runtests.py forms_tests
- 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.
- Writing the code for your ticket
- ================================
- Next we'll be adding the functionality described in ticket :ticket:`24788` to
- Django.
- Writing the code for ticket #24788
- ----------------------------------
- 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::
- 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
- 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
- correctly. To run the tests in the ``forms_tests`` folder, ``cd`` into the
- Django ``tests/`` directory and run:
- .. code-block:: console
- $ ./runtests.py forms_tests
- Oops, good thing we wrote those tests! You should still see one failure with
- the following exception::
- AssertionError: None != 'foo'
- 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::
- if prefix is not None:
- self.prefix = prefix
- Re-run the tests and everything should pass. If it doesn't, make sure you
- correctly modified the ``BaseForm`` class as shown above and copied the new test
- correctly.
- 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/``
- directory and run:
- .. code-block:: console
- $ ./runtests.py
- As long as you don't see any failures, you're good to go.
- Writing Documentation
- =====================
- 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.
- 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``::
- * A form prefix can be specified inside a form class, not only when
- instantiating a form. See :ref:`form-prefix` for details.
- 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.
- Generating a patch for your changes
- ===================================
- Now it's time to generate a patch file that can be uploaded to Trac or applied
- to another copy of Django. To get a look at the content of your patch, run the
- following command:
- .. code-block:: console
- $ git diff
- This will display the differences between your current copy of Django (with
- your changes) and the revision that you initially checked out earlier in the
- tutorial.
- Once you're done looking at the patch, hit the ``q`` key to exit back to the
- command line. If the patch's content looked okay, you can run the following
- command to save the patch file to your current working directory:
- .. code-block:: console
- $ git diff > 24788.diff
- You should now have a file in the root Django directory called ``24788.diff``.
- This patch file contains all your changes and should look this:
- .. code-block:: 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.
- So what do I do next?
- =====================
- Congratulations, you've generated your very first Django patch! Now that you've
- got that under your belt, you can put those skills to good use by helping to
- improve Django's codebase. Generating patches and attaching them to Trac
- tickets is useful, however, since we are using git - adopting a more :doc:`git
- oriented workflow </internals/contributing/writing-code/working-with-git>` is
- recommended.
- Since we never committed our changes locally, perform the following to get your
- git branch back to a good starting point:
- .. code-block:: console
- $ git reset --hard HEAD
- $ git checkout master
- 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
- simpler in nature and are great for first time contributors. Once you're
- 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
- What's next?
- ------------
- After a ticket has a patch, it needs to be reviewed by a second set of eyes.
- After uploading a patch or submitting a pull request, be sure to 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.
|