Catching missing migrations in Django

Jan 29, 2017 10:41 · 272 words · 2 minutes read django

Automation is the key to catching this.

In Django the rules of what does and does not require a migration seem entirely arbitrary to me in. For starters I always forget that verbose_name requires one. Then at some point down the road, I discover that I have missed a migration in a earlier commit. I then need to get my git-foo on to tidy things up. Here is how I use automation to solve this issue.

A little help from makemigrations

I run this in my CI environment in Django 1.9.

$ ./manage.py makemigrations --dry-run --no-input | grep 'No changes detected' || exit 1
  • --dry-run prints what it will do.
  • --no-input skips the stale content types input prompt.

Then I grep for the “No changes detected” string in the output, if it isn’t, I exit 1.

Once I make the move to Django 1.10, things become even easier with the new --check option on makemigrations:

$ ./manage.py makemigrations --check --dry-run

This exits with a non-zero error code if there are outstanding migrations. [docs]

CI or as a Unit Test?

If this is a major concern you can run this as a unit test (pytest example):

import pytest
from django.core.management import call_command

def test_no_missing_migrations():
    with pytest.raises(SystemExit) as e:
        call_command(
            'makemigrations', 
            interactive=False, 
            check=True,
            dry_run=True,
        )
    assert str(e.value) == '1'

Source

Personally, I did not want to run this check when running my tests, the call to makemigrations is somewhat slow (in my case 8 seconds) and my project test suite is still small and fast with only taking 3-4 seconds. I did not want to triple my test suite runtime just for this check.