ChatGPT解决这个技术问题 Extra ChatGPT

Disable migrations when running unit tests in Django 1.7

Django 1.7 introduced database migrations.

When running the unit tests in Django 1.7, it forces a migrate, that takes a long time. So I would like to skip the django migrations, and create the database in the final state.

I know that ignoring the migrations can be a bad practice, as that part of the code would not be tested. But that's not the case: I'm running the full migrations in the CI test server (jenkins). I only want to skip the migrations in my local tests, where the speed matters.

Some context:

Until Django 1.6, when using South, I used the SOUTH_TESTS_MIGRATE setting:

By default, South’s syncdb command will also apply migrations if it’s run in non-interactive mode, which includes when you’re running tests - it will run every migration every time you run your tests. If you want the test runner to use syncdb instead of migrate - for example, if your migrations are taking way too long to apply - simply set SOUTH_TESTS_MIGRATE = False in settings.py.

However, syncdb does not exist anymore, now it's migrate.

And from Django 1.8 I'll use the --keepdb parameter:

The --keepdb option can be used to preserve the test database between test runs. This has the advantage of skipping both the create and destroy actions which greatly decreases the time to run tests, especially those in a large test suite. If the test database does not exist, it will be created on the first run and then preserved for each subsequent run. Any unapplied migrations will also be applied to the test database before running the test suite.

So this question is limited to Django 1.7.

I'd argue that during UT, you really aren't running the migrations in a way that tests them since they DB you start out with is non-existent. Testing migrations is really only happening when you are migrating an existing DB. This 1.7 migrations business is the first real burr under the saddle I've had with Django, but it's a really big irritant. South at least got the testing scenario right for migrations.
The django-test-without-migrations package has been really handy for me, you might want to change the accepted answer to stackoverflow.com/a/28993456/200224
I prefer avoiding adding new dependencies, if possible.

m
mlissner

Look at this workaround, posted by Bernie Sumption to the Django developers mailing list:

If makemigrations has not yet been run, the "migrate" command treats an app as unmigrated, and creates tables directly from the models just like syncdb did in 1.6. I defined a new settings module just for unit tests called "settings_test.py", which imports * from the main settings module and adds this line: MIGRATION_MODULES = {"myapp": "myapp.migrations_not_used_in_tests"} Then I run tests like this: DJANGO_SETTINGS_MODULE="myapp.settings_test" python manage.py test This fools migrate into thinking that the app is unmigrated, and so every time a test database is created it reflects the current structure of models.py.

In Django 1.9, this situation is improved somewhat, and you can set the value to None:

MIGRATION_MODULES = {"myapp": None}


Note that myapp.migrations_not_used_in_tests module should not exists.
In addition to the comment @bmihelac made about the module not existing, the module string must contain the substring 'migrations', For why see: github.com/django/django/blob/stable/1.7.x/django/db/migrations/…
A gist of a function for building MIGRATION_MODULES dynamically in settings_test.py: gist.github.com/nealtodd/2869341f38f5b1eeb86d
TY. I was able to reduce my unit tests from 13 seconds to 4 seconds because of this. Also, more speed gains can be found by using sqlite for testing. For me, using postgres for tests is taking 5.5 seconds but sqlite is taking 4 seconds.
From the comments of @nealtodd's gist here's a link to a solution that avoids some of the pitfalls and is super simple: gist.github.com/NotSqrt/5f3c76cd15e40ef62d09
G
Guillaume Vincent

Here is the end of my settings file :

class DisableMigrations(object):

    def __contains__(self, item):
        return True

    def __getitem__(self, item):
        return None


TESTS_IN_PROGRESS = False
if 'test' in sys.argv[1:] or 'jenkins' in sys.argv[1:]:
    logging.disable(logging.CRITICAL)
    PASSWORD_HASHERS = (
        'django.contrib.auth.hashers.MD5PasswordHasher',
    )
    DEBUG = False
    TEMPLATE_DEBUG = False
    TESTS_IN_PROGRESS = True
    MIGRATION_MODULES = DisableMigrations()

based on this snippet

I disabled migrations only when tests are running


Nice! I would add __setitem__(self, *_) method as well because we had problem with apps that set their own migration like settings.MIGRATION_MODULES['chroniker'] = 'db_migrations'
Thank you so much for this, it's the only thing I found that actually works.
This no longer works in Django 1.9 when running tests in parallel mode. Using normal non-parallel testing, it continues to work fine, but switching to parallel mode results in errors that tables are not being found.
@LeeSemel in parallel mode you probably want to use the solution from rlmv
@guillaumevincent i have the same problem when using django-test-without-migrations in parallel mode
r
rlmv

django-test-without-migrations adds a --nomigrations flag to manage.py test. Works like a charm.


C
Community

Update: Never mind, this change was reverted before 1.10 final was released. Hopefully it will return in a future version.

Note that as of Django 1.10 this can be controlled by a test database setting.

MIGRATE Default: True If set to False, Django won’t use migrations to create the test database.


F
FavorMylikes

I just figure out how to disable migrations after django 1.10,may be it could help for somebody. Here is link at git

class DisableMigrations(dict):
    def __contains__(self, item):
        return True

    def __getitem__(self, item):
        return None

DATABASES = DisableMigrations()

MIGRATION_MODULES = DisableMigrations()

Migrations for django 1.10 has two part,please look at load_disk and recorder

The part of load_disk for migrations model of app that be added at INSTALL_APP And the part of recorder for database connection For the version before 1.9 we need set MIGRATION_MODULES={'do.not.migrate':'notmigrations'} when you are running test Now we need set it None like MIGRATION_MODULES={'do.not.migrate':None} So if we do not want make migrations for any app, just extend a dict and return None for getitem function , and do the same at DATABASES, that is the right thing you need to do

PS: For command, you need to specify --setting=module.path.settings_test_snippet after test PPS If you are working with pycharm ,do not set --settings options at Run/Debug configurations, just add path of settings_test_snippet.py at Custom setting. That just be fine!!

enjoy


D
David Arcos

https://gist.github.com/apollovy/22826f493ad2d06d9a9a22464730ce0b

MIGRATION_MODULES = {
    app[app.rfind('.') + 1:]: 'my_app.migrations_not_used_in_tests'
    for app in INSTALLED_APPS
}

Welcome to stackoverflow. please have a look at the tour and help center. It is generally encouraged to not only provide one-line-answers, but also to explain why (you think) your answer is correct.
d
devsnd

For django 1.9 and up the answer of Guillaume Vincent does not work anymore, so here's a new solution:

I'm using this snippet in my settings file, after the definition of the INSTALLED_APPS

if os.environ.get('TESTS_WITHOUT_MIGRATIONS', False):
    MIGRATION_MODULES = {
        app.split('.')[-1]: None for app in INSTALLED_APPS
    }

It iterates over all installed apps and marks each as having no migration module. See the django docs for more information.

Using this snippet you can run your tests, setting the environment variable TESTS_WITHOUT_MIGRATIONS, e.g.:

TESTS_WITHOUT_MIGRATIONS=1 ./manage.py test

Should anyone want to do that directly in your command line, you could type this before launching your tests: export TESTS_WITHOUT_MIGRATIONS=1
L
LMB

From Django version 3.1 and up the correct way to do this is to use the MIGRATE setting in the database settings dict. See also the documentation.

#settings.py
DATABASES = {
    'TEST': {
        'NAME': 'Foo',
        'MIGRATE': False
    }
}