Django / Python Code Standards

These documents are very good starting points:



We are going to start by aiming for 70% test coverage for an app and 50% for a project. Useful to bear these in mind Changing the Metrics Conversation and The many flaws of test coverage

Add the following to requirements/local.txt:


Add configuration for flakes e.g in setup.cfg:

addopts= --ds=settings.dev_test --cov-report html --reuse-db --fail-on-template-vars
norecursedirs = .git venv-* src node_modules
# 1. migrations always import models
# 2. custom settings files e.g. '' do 'from .base import *'
# 3. '' py.test fixtures conflict with pyflakes
flakes-ignore =
    block/migrations/* UnusedImport
    example_block/dev_*.py ImportStarUsed UnusedImport RedefinedWhileUnused


Change block to the correct path for your app or project.


DJANGO_SETTINGS_MODULE was being ignored. To fix, add addopts= --ds=settings.dev_test --cov-report html... to addopts and remove DJANGO_SETTINGS_MODULE.

To format the code:

white path/to/the/


Create a separate commit for code formatting.

To check the code:

py.test --flakes
py.test --pep8


GIT Commit Messages


The seven rules of a great git commit message:

  1. Separate subject from body with a blank line

  2. Limit the subject line to 50 characters

  3. Capitalize the subject line

  4. Do not end the subject line with a period

  5. Use the imperative mood in the subject line

  6. Wrap the body at 72 characters

  7. Use the body to explain what and why vs. how


And my favourite piece of advice:

A properly formed git commit subject line should always be able to complete the following sentence:

If applied, this commit will your subject line here


The order of model inner classes and standard methods should be as follows (they are not all required):

  • All database fields

  • Custom manager attributes

  • class Meta

  • def __unicode__()

  • def __str__()

  • def save()

  • def get_absolute_url()

  • Any custom methods



We don’t delete data (unless there is a specific requirement for it).

We have a TimedCreateModifyDeleteModel which sets up the fields.


To mark an object as deleted in a Django view, see UpdateView not DeleteView

File and Image Field

For a FileField or ImageField, then set the upload_to path to reflect the app and model name e.g. for the document field in the Attachment model in the mail app:

document = models.FileField(upload_to='mail/attachment/')


Continuous Integration

Create a .gitlab-ci.yml file in the root of the project e.g:

Create a requirements/ci.txt file e.g:


To make access easier for public repositories, use the GIT https URL rather than a path to the folder e.g: -e git+

Check the project setup.cfg file to make sure src is included in the norecursedirs section e.g:

norecursedirs = .git angular venv-* src
# or
norecursedirs = .git venv-* src node_modules
# or
norecursedirs = .git venv-* src

Commit and push to GitLab.

In GitLab logged in as the project owner:

  • Select: Settings | General and expand the Permissions section and set: Repository, Pipelines to Only Project Members.

  • Select: Settings, CI/CD Pipelines and expand the Runners section and: Disable shared runners for this project.

  • Still in Settings, CI/CD Piplines expand the “General Pipeline settings and set Test coverage parsing to \d+\%\s*$ (copy the pytest-cov (Python) example).

For email notifications for the project, Settings, Integrations, Pipelines emails. Tick Active, add Recipients and Add pusher


Test settings on Builds emails threw an error last time I tried, but the email still sends OK.

Pipelines… create…


To cleanup Docker containers, see Docker.


Model factories should create the minimum required to construct a valid object e.g. a product will probably need to create a product category, but a contact will not need to fill in the date of birth.


I am not 100% sure about this… but I am sure a factory which does more than it needs to will make it feel like magic is going on and cause confusion.


We use the unittest.mock module:


import attr
from unittest.mock import patch

class StripeCustomer:
    id = attr.ib()

@patch("stripe.Customer.create", return_value=StripeCustomer(id="xyz"))
def test_payment(mock_charge, mock_customer, client):
    assert mock_charge.called is True
    assert mock_customer.called is True


We also use @mock.patch.multiple.

Following Cameron Maske’s tweet ref So many different ways to mock an HTTP request in Python, I am now testing the responses library for mocking HTTP requests…


The following (ref httmock) is an old note…

When we next do HTTP/API testing, then checkout


Create a DjangoModelFactory for the model using Factory Boy and test the following (these are a common source of hard to diagnose issues):

  • ordering

  • str

To mock a model manager:

from unittest import mock

with mock.patch('editor.models.CatArticle.objects') as m:
    m.return_value = {}


From Coding Conventions:


the preferred and wonderfully explicit Jacob Kaplan-Moss / Frank Wiles pattern


Probably best to use the actual view class rather than just the name, using view='polls.views.standard.poll_list',, makes it harder to debug on errors.