Keeping on top of Django's New Migrations
How to avoid being caught out by a missing Migration
For managing database schemas, as great and well-loved as South was, Django's new migrations framework is nicer, particularly thanks to better support for multi-developer, multi-branch projects like YJ.
However, the greater attention now paid to a model's changes has a potential gotcha, especially in a multi-dev environment: even changing a help_text attribute on a field is now considered migration-worthy, because Django wants to keep as accurate a snapshot as possible of the model's fields. This means that it's actually easier to end up with subtle model changes that you forget to capture in  a migration, particularly if you're more used to South's 'lower-resolution' view of model changes.
So, what's the problem here? A developer generating a new migration as part of their work may find it actually includes more than their intended changes, because someone gently amended the model previously but didn't create a migration. This can also lead to Django complaining about a mismatch between models and migrations when running manage.py migrate when deploying, etc. 
To help avoid such annoyances, we've added a check to the YJ test suite. Here's more or less our test, based heavily on the code Django runs to spot out-of-sync models. Feel free to add it to your own project.
from django.apps import apps
from django.db import connections, DEFAULT_DB_ALIAS
from django.db.migrations.autodetector import MigrationAutodetector
from django.db.migrations.executor import MigrationExecutor
from django.db.migrations.state import ProjectState
from django.test import TestCase
class MigrationHealthCheck(TestCase):
    """
    Try to pre-empt migration woes.
    """
    def migration_progress_callback(*args, **kwargs):
        # This is a no-op to keep the MigrationExecutor's
        # constructor happy
        pass
    def test_for_uncreated_migrations(self):
        connection = connections[DEFAULT_DB_ALIAS]
        # Work out which apps have migrations and which do not
        executor = MigrationExecutor(
            connection, 
            self.migration_progress_callback
        )
        autodetector = MigrationAutodetector(
            executor.loader.project_state(),
            ProjectState.from_apps(apps),
        )
        changes = autodetector.changes(graph=executor.loader.graph)
        if changes:
            self.fail(
                "Your models have changes that are not yet reflected "
                "in a migration. You should add them now. "
                "Relevant app(s): %s" % changes.keys()
            )
Tech Lead, Django Dev
Posted in: django
