DEV Community

Annabelle Wiegart
Annabelle Wiegart

Posted on

Your first django PR - from scratch to improved patch

A complete walkthrough

You are ready to contribute code to Django, but you feel overwhelmed by the contributing documentation? You wish there was a step-by-step walkthrough from setting up your local environment to making your first pull request? This tutorial is for you.

I recently made my first pull request to Django. Actually, it was the first pull request I ever made, since I had mainly worked on one-person-projects before. As I went along,I documented the steps, including links to the relevant documentation pages. I started from an existing patch that needed improvement, which is an approach that I recommend for your first ticket.

In this tutorial, we will focus on the git/github workflow. For additional aspects, such as working with Django's ticket system, please also see Rim Choi's blog post. We will go through the following steps:

Setting up your local environment

Getting a copy of Django’s development version

As described in Django's contribution tutorial, fork the Django repo on GitHub. Then, clone your Django fork to your local machine:
git clone https://github.com/YourGitHubName/django.git

Your Django fork is automatically added as a remote repository named "origin". You can verify this with the git remote command:
git remote -v

As described in Working with Git and GitHub, you need to add the original Django repository as an additional remote. It's a good convention to call it "upstream":
git remote add upstream https://github.com/django/django.git

If you have not done so already with git config --global, configure your username and email address for your local Django git repository:
git config user.name <your name>
git config user.email <your email>

Creating a virtual environment

Create and activate a virtual environment, as described in the contribution tutorial. Inside your virtual environment, install your local copy of Django in editable mode:
pip install -e /path/to/your/local/clone/django/

Install the test dependencies:
cd tests
pip install -r requirements/py3.txt

If the installation fails, the cause could be a missing system-level dependency. Just as an example, on Ubuntu 22.04, I had to install the system package libmemcached-dev:
sudo apt install libmemcached-dev

After installing the missing system packages, retry the installation of the test dependencies.

Once the test dependencies are installed, you should be able to run Django's test suite:
./runtests.py

The Django documentation has its own dependencies. You will likely need them later, so go ahead and install them:
cd ../docs
pip install -r requirements.txt

Now, change back to the top-level folder of your Django repository, e.g. with cd ... Install the pre-commit hooks:
pip install pre-commit
pre-commit install

Finding a ticket

Finding a beginner-friendly ticket to work on can seem a bit challenging at first. There is an easy pickings filter, but tickets with this category are rare and often already assigned. I would recommend you to find the easiest ticket possible, so you can focus on the contribution workflow first. And if your first ticket is a quick success, you will be more motivated to keep going.

In her excellent video tutorial Your first Django contribution, Django fellow Sarah Boyce recommends the so-called "vulture strategy", i.e. working on a patch from a previously submitted pull request that needs improvement. Her filters are:

  • Triage stage = Accepted
  • Has patch = Yes
  • Patch needs improvement = Yes
  • Modified = between "-" and 6 months ago
  • Type = Bug

Django ticket filters for patches that need improvement

It can also be helpful to group the resulting ticket list by component:

Option in Django's ticket system to group results by component

When you decide to work on a ticket, please assign it to yourself. If you are unsure whether someone else is currently working on the ticket, you can leave a comment asking if you can take over. If you don't get a response within 48 h, assigning the ticket to yourself is probably fine. See also "Claiming" tickets.

Working on your ticket

The first thing you'll want to do is to create a local branch for your ticket that is based on the upstream main branch:
git checkout -b ticket_<ticket number> upstream/main

You chose a ticket with an existing patch that needs improvement. There may be several pull requests associated with your ticket. You should always base your work on the most recent patch, as explained in Sarah Boyce's video.

Here's one way to get the existing patch into your local branch:
curl -L https://github.com/django/django/pull/xxxxx.patch | git am
See also Working on a patch.

For reproducing errors and testing your changes, you should work with a Django test project which uses the same virtual environment that you just set up, with your local Django copy installed in editable mode.

Once you made your changes, you'll probably want to do a commit. That's when the pre-commit hooks are activated. Their purpose is to catch as many errors before you push them to the remote repository. For instance, your code will be formatted with black. You'll need to re-stage the changes made by the pre-commit hooks and retry the commit. You might have to manually fix some errors, too. Here is an example error message for a comment line that is too long:

flake8...................................................................Failed
\- hook id: flake8
\- exit code: 1
django/conf/locale/de_CH/formats.py:28:80: W505 doc line too long (80 > 79 characters)

In this example, I needed to shorten my comment line to a maximum of 79 characters.

Going through the contribution checklist

At this point, you have made one or several commits on your ticket branch to improve the existing patch.You have addressed all the comments in the ticket and in the previous pull request. You think your changes are ready for a new pull request.

That's when you need to go through the contribution checklist. For your case, i.e. a bug fix that needs improvement, probably all sections are relevant except New Feature and Deprecating a feature. I'll pick out a few steps, but please do read the whole checklist and go through all the points that apply.

Before you go through the contribution checklist, check if the upstream main branch has changed in the meantime. If so, please rebase your work:
git fetch upstream
git rebase upstream/main

Now, check whether the documentation builds without any errors. From your local Django directory:
cd docs
make html

Back inside the top-level Django directory, make sure the test suite passes:
cd tests
./runtests.py

Squash your commits and the commits of previous contributors of the patch into one single commit. Check how many commits you need to edit:
git log -n 10 --oneline
Let's say you want to squash 3 commits into one:
git rebase -i HEAD~3

Git editor showing the commits to be rebased

Leave the word pick next to your last commit. Replace pick with squash next to the other two commits.

Save the file. git will open a new file for you with the combined commit messages. Edit them so they match Django's commit message format. Don't forget to mention the co-authors of the patch, i.e. the authors of the previous patch which you improved. Example:
Fixed #35095 -- Clarified Swiss number formatting in docs/topics/i18n/formatting.txt.
Co-Authored-By: Name <email@address>

Now you can push your squashed commit to your remote fork:
git push origin ticket_<ticket number>

If you messed up the commit message, e.g. you forgot the final period, and you already pushed your changes to your Django fork, you can rewrite the commit message like so:
git rebase -i HEAD~1

Write "reword" next to your commit in the text editor:

Git editor showing the commit to be rebased

Save the file. A new file opens where you can edit your commit message. Make your changes, and save this second file, too. The commit message is now rewritten.
If you had pushed the changes already to your Django fork, you need to force-push the modified commit:
git push --force origin <your branch>

If this is your first Django contribution, please add yourself to the AUTHORS file and submit a Contributor License Agreement.

Creating the pull request

Now it's finally time to make your pull request! As described in the contributing tutorial, go to Django's GitHub page, or call the URL https://github.com/GITHUB_USERNAME/pull/new/BRANCH_NAME/, substituting GITHUB_USERNAME with your actual username, and BRANCH_NAME with the name of your branch.

Enter a description for the pull request. Mention the ticket number as suggested in the comment. If your patch is based on a previous pull request, mention it in the description. See my example description.

Make sure your ticket appears in the review queue.

If some GitHub checks fail, you can check the details of the failure via the three little dots next to the test:

Django's GitHub CI tests

If some linting- or formatting-related checks fail and you can fix the issue immediately, it is okay to force-push a rewritten commit. In other circumstances, you should avoid force-pushing since it can erase valuable context for the reviewer.

While your code is being reviewed, you may experience several iterations of the following cycle:

  • accept review feedback
  • add commits to your local branch
  • squash all new local commits into one review commit
  • push the review commit to your remote branch

If you are looking for friendly Djangonauts willing to help you, go to the #contributing-getting-started channel on the Django Discord server.

Don't give up :) I hope your PR gets merged!

Top comments (1)

Collapse
 
ontowhee profile image
ontowhee

Excellent write up, Annabelle! I love how thoroughly you've documented the steps while making it very approachable!