How to make Wagtail project have good coding style

Michael Yin

Last updated on December 18 2021

Table of Contents

Wagtail Tutorial Series:

  1. Create Wagtail Project
  2. Dockerizing Wagtail App
  3. Add Blog Models to Wagtail
  4. How to write Wagtail page template
  5. Add Bootstrap Theme to Wagtail
  6. How to use StreamField in Wagtail
  7. Wagtail Routable Page
  8. Add pagination component to Wagtail
  9. Customize Wagtail Page URL
  10. Add Full Text Search to Wagtail
  11. Add Markdown Support to Wagtail
  12. Add LaTeX Support & Code Highlight In Wagtail
  13. How to Build Form Page in Wagtail
  14. How to Create and Manage Menus in Wagtail
  15. Wagtail SEO Guide
  16. Source code: https://github.com/AccordBox/wagtail-bootstrap-blog

Wagtail Tips:

  1. Wagtail Tip #1: How to replace ParentalManyToManyField with InlinePanel
  2. Wagtail Tip #2: How to Export & Restore Wagtail Site

Write style in Wagtail:

  1. How to use SCSS/SASS in your Django project (Python Way)
  2. How to use SCSS/SASS in your Django project (NPM Way)

Other Wagtail Topics:

  1. How to make Wagtail project have good coding style
  2. How to do A/B Testing in Wagtail CMS 
  3. How to build a landing page using Wagtail CMS 
  4. How to support multi-language in Wagtail CMS 

More Wagtail articles and eBooks written by me

Introduction

In this Wagtail tutorial, I will teach you how to have clean coding style for your Wagtail/Django project. After reading this article, you will get:

  1. How to check if your Wagtail project follows the PEP8 guidelines.
  2. How to auto fix your Wagtail project to make it follow PEP8 using autopep8
  3. How to organize your Wagtail project import statements using isort
  4. How to format Python code with Black
  5. What is pre-commit and how to use it.

PEP8

PEP8 is a document that provides guidelines and best practices on how to write Python code.

If your Python code is following the guidelines of PEP8, then your Python code would be easy to read and maintain.

Here we use pycodestyle (formerly called pep8) to let it helps us check our Python code.

pycodestyle is a tool to check your Python code against some of the style conventions in PEP 8.

Let's first create a test python file test.py, as you can see, this file has bad coding style and not easy to read.

import math, sys;

def example1():
    ####This is a long comment. This should be wrapped to fit within 72 characters.
    some_tuple=(   1,2, 3,'a'  );
    some_variable={'long':'Long code lines should be wrapped within 79 characters.',
    'other':[math.pi, 100,200,300,9876543210,'This is a long string that goes on'],
    'more':{'inner':'This whole logical line should be wrapped.',some_tuple:[1,
    20,300,40000,500000000,60000000000000000]}}
    return (some_tuple, some_variable)
def example2(): return {'has_key() is deprecated':True}.has_key({'f':2}.has_key(''));
class Example3(   object ):
    def __init__    ( self, bar ):
     #Comments should have a space after the hash.
     if bar : bar+=1;  bar=bar* bar   ; return bar
     else:
                    some_string = """
                       Indentation in multiline strings should not be touched.
Only actual code should be reindented.
"""
                    return (sys.path, some_string)

Now we use pycodestyle to check the above file.

$ pip install pycodestyle

$ pycodestyle test.py
test.py:1:12: E401 multiple imports on one line
test.py:1:17: E703 statement ends with a semicolon
test.py:3:1: E302 expected 2 blank lines, found 1
test.py:4:5: E265 block comment should start with '# '
test.py:4:80: E501 line too long (83 > 79 characters)
test.py:5:15: E225 missing whitespace around operator
test.py:5:17: E201 whitespace after '('
...

pycodestyle give us some output and we can fix the Python file based on the output.

There are some other tools which can do similar jobs and here I recommend you to try flake8

flake8

Flake8 is a wrapper of pycodestyle and it also add some more useful features.

$ pip install flake8

$ flake8 fabfile.py
test.py:1:12: E401 multiple imports on one line
test.py:1:17: E703 statement ends with a semicolon
test.py:3:1: E302 expected 2 blank lines, found 1
test.py:4:5: E265 block comment should start with '# '
test.py:5:15: E225 missing whitespace around operator
test.py:5:17: E201 whitespace after '('
test.py:5:21: E231 missing whitespace after ','
...

Here you might see flake8 and pycodestyle print the similar output, but I recommend you to use flake8 because it provides more features compared with pycodestyle

How to config flake8 check rule

By default, the max-line of Python file is 80, If you want to change the max-line length to 120 instead of 80. You can create setup.cfg in the root of your project. setup.cfg can contain many config sections for different tools, we can put them in one single file.

Below is a sample config file.

[flake8]
ignore=E501,C901,F401
exclude = */migrations/*,node_modules,*/settings/*,

max-line-length = 120

The exclude would tell flake8 to ignore some directory and ignore would tell it to ignore some errors. For example, if you have imported some module but did not use it in your code, you will see something like this. F401, module imported but unused. Then you can add F401 to your setup.cfg to let it pass.

If you want pyflake8 to ignore some lines in python file, you can append # noqa just like this.

from .base import *  # noqa

# noqa tells pyflake8 to not check this line, this is commonly used in Wagtail settings file.

Now you can try run flake8 for your Django/Wagtail project.

# cd to the root of the project
$ flake8 django_app

Autopep8

If you are new to Python world, you might see long output after you run flake8 command, is there something that can help you solve this?

autopep8 can save you!

$ pip install autopep8

$ autopep8 test.py --in-place --aggressive --aggressive

Now you can see the test.py has been reformatted with clean style.

import math
import sys


def example1():

    # This is a long comment. This should be wrapped to fit within 72
    # characters.
    some_tuple = (1, 2, 3, 'a')
    some_variable = {
        'long': 'Long code lines should be wrapped within 79 characters.',
        'other': [
            math.pi,
            100,
            200,
            300,
            9876543210,
            'This is a long string that goes on'],
        'more': {
            'inner': 'This whole logical line should be wrapped.',
            some_tuple: [
                1,
                20,
                300,
                40000,
                500000000,
                60000000000000000]}}
    return (some_tuple, some_variable)


def example2(): return ('' in {'f': 2}) in {'has_key() is deprecated': True}


class Example3(object):
    def __init__(self, bar):
        # Comments should have a space after the hash.
        if bar:
            bar += 1
            bar = bar * bar
            return bar
        else:
            some_string = """
                       Indentation in multiline strings should not be touched.
Only actual code should be reindented.
"""
            return (sys.path, some_string)

You can also let autopep8 to help you fix the whole directory

autopep8 project_dir --recursive --aggressive --in-place --verbose

Please make sure you have two --aggressive in your command, the file would not be fixed if you only have one --aggressive

Black

Black is the uncompromising Python code formatter. By using it, you agree to cede control over minutiae of hand-formatting. In return, Black gives you speed, determinism, and freedom from pycodestyle nagging about formatting. You will save time and mental energy for more important matters.

You can see black as another tool which help you Python code has good coding style, which is more and more popular in Python community.

$ pip install black
$ black django_app

reformatted xxx.py

isort

When coding with Wagtail, most code are coming from django, wagtail, and some are coming from other 3-party packages. isort can help us better manage the import statement.

$ pip install isort
$ isort --recursive project_dir --skip django_app/wsgi.py --skip-glob "*/migrations/*" --skip-glob "*/node_modules/*"

Prettier

Now we already know how to make our Python code have good style using flake8, black and isort

But in Wagtail projects, sometimes we also need to write CSS, SCSS and JS code.

So we should use Prettier to help us.

pre-commit

pre-commit is a framework for managing and maintaining multi-language pre-commit hooks.

Let's create .pre-commit-config.yaml at root directory

repos:
  - repo: https://gitlab.com/pycqa/flake8
    rev: 4.0.1               # you can change the version as you like
    hooks:
      - id: flake8
        args: ['--config=setup.cfg']

  - repo: https://github.com/PyCQA/isort
    rev: 5.9.3
    hooks:
      - id: isort

  - repo: https://github.com/pre-commit/mirrors-prettier
    rev: v2.1.2
    hooks:
      - id: prettier
        types: [javascript]
$ pip install pre-commit

# install git hook
$ pre-commit install

$ pre-commit run --all-files

flake8...................................................................Passed
isort....................................................................Passed
prettier.................................................................Passed
  1. Everytime we run git commit, pre-commit will run to check the commit files automatically.
  2. pre-commit can works with any programming language (prettier in this case)
  3. Wagtail also use this solution https://github.com/wagtail/wagtail/blob/v2.15.1/.pre-commit-config.yaml

Check Python code style in CI job

With pre-commit, we can run pre-commit run --all-files in CI or use https://pre-commit.ci/ service.

Node.js

Another thing I want to say is pre-commit has dependency nodeenv, which will create an isolated node.js env automatically.

So if you run pre-commit command in a pure python env, the prettier can still work as expected.

Wagtail Tutorial Series:

  1. Create Wagtail Project
  2. Dockerizing Wagtail App
  3. Add Blog Models to Wagtail
  4. How to write Wagtail page template
  5. Add Bootstrap Theme to Wagtail
  6. How to use StreamField in Wagtail
  7. Wagtail Routable Page
  8. Add pagination component to Wagtail
  9. Customize Wagtail Page URL
  10. Add Full Text Search to Wagtail
  11. Add Markdown Support to Wagtail
  12. Add LaTeX Support & Code Highlight In Wagtail
  13. How to Build Form Page in Wagtail
  14. How to Create and Manage Menus in Wagtail
  15. Wagtail SEO Guide
  16. Source code: https://github.com/AccordBox/wagtail-bootstrap-blog

Wagtail Tips:

  1. Wagtail Tip #1: How to replace ParentalManyToManyField with InlinePanel
  2. Wagtail Tip #2: How to Export & Restore Wagtail Site

Write style in Wagtail:

  1. How to use SCSS/SASS in your Django project (Python Way)
  2. How to use SCSS/SASS in your Django project (NPM Way)

Other Wagtail Topics:

  1. How to make Wagtail project have good coding style
  2. How to do A/B Testing in Wagtail CMS 
  3. How to build a landing page using Wagtail CMS 
  4. How to support multi-language in Wagtail CMS 

More Wagtail articles and eBooks written by me


Michael Yin

Michael is a Full Stack Developer from China who loves writing code, tutorials about Django, and modern frontend tech.

He has published some ebooks on leanpub and tech course on testdriven.io.

He is also the founder of the AccordBox which provides the web development services.


Table of Contents

Frontend Guide For Python Dev

This FREE guide help Python developers to learn the Modern frontend tech

Learn More

Build Jamstack web app with Next.js and Wagtail CMS.

Read More

Subscribe

Get notified about new great Web Development Tutorial