Create a gist now

Instantly share code, notes, and snippets.

Skip migrations for a Django 1.7 test run
# If your test settings file doesn't import any other settings file
# then you can use the function directly:
def prevent_tests_migrate(db):
import django
from django.db import connections
from django.db.migrations.executor import MigrationExecutor
django.setup()
ma = MigrationExecutor(connections[db]).loader.migrated_apps
return dict(zip(ma, ['{a}.notmigrations'.format(a=a) for a in ma]))
MIGRATION_MODULES = prevent_tests_migrate('default')
# If your test settings file imports another settings file (typically
# your base settings file, then the function won't work as the settings
# won't be fully in scope by the time setup() is called. Depending on
# you settings layout you'll an error like:
# "ImproperlyConfigured: The SECRET_KEY setting must not be empty."
# or ""Apps aren't loaded yet", or "RuntimeError: populate() isn't reentrant"
#
# Instead, simply use the function in the Django shell to generate the
# dictionary and hardcode it to MIGRATION_MODULES in the test settings.
# Since Django is already set up in the shell the function can just be:
def prevent_tests_migrate(db):
from django.db import connections
from django.db.migrations.executor import MigrationExecutor
ma = MigrationExecutor(connections[db]).loader.migrated_apps
return dict(zip(ma, ['{a}.notmigrations'.format(a=a) for a in ma]))
# You'll need to manually update MIGRATION_MODULES if you add new apps
# with migrations and want them skipped as well.
#
# Also, if you suddenly come across an error like this when Django starts up:
# "Migration foo.0001_initial dependencies reference nonexistent
# parent node (u'bar', u'0001_initial')"
# then it probably means you've added an app (maybe 3rd party) that has a
# dependency on one of the apps in MIGRATION_MODULES that you've skipped
# (e.g. a dependency on the contenttypes app). To fix, simply add the new
# app to MIGRATION_MODULES.
@nealtodd
Owner

Pretends migrated apps aren't migrated by giving them a fake migration module (so syncdb behaviour is used instead). Mimics the old SOUTH_TESTS_MIGRATE = False behaviour in South.

MIGRATION_MODULES could be a hard-coded dictionary of known apps. This function simply does it dynamically.

This is intended for 1.7 only, pending the --keepdb flag in Django 1.8. It's also only intended for faster running of a single test, not a whole testsuite run (which, correctly, should use migrations as Andrew Goodwin intended it to).

@n1207n
n1207n commented Sep 25, 2014

This is awesome. I was bogged down by the unit tests trying to run the migrations first to create a test db. It's working greatly.

Thanks!

@mpyatishev

Thanks!

@martync
martync commented Sep 29, 2014

Placing those lines in settings.py gives me
django.core.exceptions.ImproperlyConfigured: The SECRET_KEY setting must not be empty.

Trying to figure this out.

@nealtodd
Owner
nealtodd commented Oct 1, 2014

@martync - that'll probably be because you need to define SECRET_KEY before calling prevent_tests_migrate in settings.py because the django.setup() call needs it.

@NotSqrt
NotSqrt commented Nov 20, 2014

The code I come up with : https://gist.github.com/NotSqrt/5f3c76cd15e40ef62d09
No strange error, because no django.setup()

@nealtodd
Owner

Was just adding a comment as to why using this function directly sometimes might not work (i.e. when the test settings file imports another settings file) and saw the code from @NotSqrt.

Now that's an elegant solution!

I focused too much on replicating how Django generates its default MIGRATION_MODULES, with a fake module name, rather than faking the method calls on the dictionary.

Thanks @NotSqrt, I'll adopt your approach. (Should be fine as long as none creates an app called notmigrations ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment