Code Monkey home page Code Monkey logo

django-test-migrations's Introduction

django-test-migrations

wemake.services Build status codecov Python Version PyPI - Django Version wemake-python-styleguide

Features

  • Allows to test django schema and data migrations
  • Allows to test both forward and rollback migrations
  • Allows to test the migrations order
  • Allows to test migration names
  • Allows to test database configuration
  • Fully typed with annotations and checked with mypy, PEP561 compatible
  • Easy to start: has lots of docs, tests, and tutorials

Read the announcing post. See real-world usage example.

Installation

pip install django-test-migrations

We support several django versions:

  • 3.2
  • 4.1
  • 4.2
  • 5.0

Other versions most likely will work too, but they are not officially supported.

Testing Django migrations

Testing migrations is not a frequent thing in django land. But, sometimes it is totally required. When?

When we do complex schema or data changes and what to be sure that existing data won't be corrupted. We might also want to be sure that all migrations can be safely rolled back. And as a final touch, we want to be sure that migrations are in the correct order and have correct dependencies.

Testing forward migrations

To test all migrations we have a Migrator class.

It has three methods to work with:

  • .apply_initial_migration() which takes app and migration names to generate a state before the actual migration happens. It creates the before state by applying all migrations up to and including the ones passed as an argument.

  • .apply_tested_migration() which takes app and migration names to perform the actual migration

  • .reset() to clean everything up after we are done with testing

So, here's an example:

from django_test_migrations.migrator import Migrator

migrator = Migrator(database='default')

# Initial migration, currently our model has only a single string field:
# Note:
# We are testing migration `0002_someitem_is_clean`, so we are specifying
# the name of the previous migration (`0001_initial`) in the
# .apply_initial_migration() method in order to prepare a state of the database
# before applying the migration we are going to test.
#
old_state = migrator.apply_initial_migration(('main_app', '0001_initial'))
SomeItem = old_state.apps.get_model('main_app', 'SomeItem')

# Let's create a model with just a single field specified:
SomeItem.objects.create(string_field='a')
assert len(SomeItem._meta.get_fields()) == 2  # id + string_field

# Now this migration will add `is_clean` field to the model:
new_state = migrator.apply_tested_migration(
    ('main_app', '0002_someitem_is_clean'),
)
SomeItem = new_state.apps.get_model('main_app', 'SomeItem')

# We can now test how our migration worked, new field is there:
assert SomeItem.objects.filter(is_clean=True).count() == 0
assert len(SomeItem._meta.get_fields()) == 3  # id + string_field + is_clean

# Cleanup:
migrator.reset()

That was an example of a forward migration.

Backward migration

The thing is that you can also test backward migrations. Nothing really changes except migration names that you pass and your logic:

migrator = Migrator()

# Currently our model has two field, but we need a rollback:
old_state = migrator.apply_initial_migration(
    ('main_app', '0002_someitem_is_clean'),
)
SomeItem = old_state.apps.get_model('main_app', 'SomeItem')

# Create some data to illustrate your cases:
# ...

# Now this migration will drop `is_clean` field:
new_state = migrator.apply_tested_migration(('main_app', '0001_initial'))

# Assert the results:
# ...

# Cleanup:
migrator.reset()

Testing migrations ordering

Sometimes we also want to be sure that our migrations are in the correct order and that all our dependencies = [...] are correct.

To achieve that we have plan.py module.

That's how it can be used:

from django_test_migrations.plan import all_migrations, nodes_to_tuples

main_migrations = all_migrations('default', ['main_app', 'other_app'])
assert nodes_to_tuples(main_migrations) == [
    ('main_app', '0001_initial'),
    ('main_app', '0002_someitem_is_clean'),
    ('other_app', '0001_initial'),
    ('main_app', '0003_update_is_clean'),
    ('main_app', '0004_auto_20191119_2125'),
    ('other_app', '0002_auto_20191120_2230'),
]

This way you can be sure that migrations and apps that depend on each other will be executed in the correct order.

factory_boy integration

If you use factories to create models, you can replace their respective .build() or .create() calls with methods of factory and pass the model name and factory class as arguments:

import factory

old_state = migrator.apply_initial_migration(
    ('main_app', '0002_someitem_is_clean'),
)
SomeItem = old_state.apps.get_model('main_app', 'SomeItem')

# instead of
# item = SomeItemFactory.create()
# use this:
factory.create(SomeItem, FACTORY_CLASS=SomeItemFactory)

# ...

Test framework integrations 🐍

We support several test frameworks as first-class citizens. That's a testing tool after all!

Note that the Django post_migrate signal's receiver list is cleared at the start of tests and restored afterwards. If you need to test your own post_migrate signals then attach/remove them during a test.

pytest

We ship django-test-migrations with a pytest plugin that provides two convenient fixtures:

  • migrator_factory that gives you an opportunity to create Migrator classes for any database
  • migrator instance for the 'default' database

That's how it can be used:

import pytest

@pytest.mark.django_db()
def test_pytest_plugin_initial(migrator):
    """Ensures that the initial migration works."""
    old_state = migrator.apply_initial_migration(('main_app', None))

    with pytest.raises(LookupError):
        # Model does not yet exist:
        old_state.apps.get_model('main_app', 'SomeItem')

    new_state = migrator.apply_tested_migration(('main_app', '0001_initial'))
    # After the initial migration is done, we can use the model state:
    SomeItem = new_state.apps.get_model('main_app', 'SomeItem')
    assert SomeItem.objects.filter(string_field='').count() == 0

unittest

We also ship an integration with the built-in unittest framework.

Here's how it can be used:

from django_test_migrations.contrib.unittest_case import MigratorTestCase

class TestDirectMigration(MigratorTestCase):
    """This class is used to test direct migrations."""

    migrate_from = ('main_app', '0002_someitem_is_clean')
    migrate_to = ('main_app', '0003_update_is_clean')

    def prepare(self):
        """Prepare some data before the migration."""
        SomeItem = self.old_state.apps.get_model('main_app', 'SomeItem')
        SomeItem.objects.create(string_field='a')
        SomeItem.objects.create(string_field='a b')

    def test_migration_main0003(self):
        """Run the test itself."""
        SomeItem = self.new_state.apps.get_model('main_app', 'SomeItem')

        assert SomeItem.objects.count() == 2
        assert SomeItem.objects.filter(is_clean=True).count() == 1

Choosing only migrations tests

In CI systems it is important to get instant feedback. Running tests that apply database migration can slow down tests execution, so it is often a good idea to run standard, fast, regular unit tests without migrations in parallel with slower migrations tests.

pytest

django_test_migrations adds migration_test marker to each test using migrator_factory or migrator fixture. To run only migrations test, use -m option:

pytest -m migration_test  # Runs only migration tests
pytest -m "not migration_test"  # Runs all except migration tests

unittest

django_test_migrations adds migration_test tag to every MigratorTestCase subclass. To run only migrations tests, use --tag option:

python mange.py test --tag=migration_test  # Runs only migration tests
python mange.py test --exclude-tag=migration_test  # Runs all except migration tests

Django Checks

django_test_migrations comes with 2 groups of Django's checks for:

  • detecting migrations scripts automatically generated names
  • validating some subset of database settings

Testing migration names

django generates migration names for you when you run makemigrations. These names are bad (read more about why it is bad)! Just look at this: 0004_auto_20191119_2125.py

What does this migration do? What changes does it have?

One can also pass --name attribute when creating migrations, but it is easy to forget.

We offer an automated solution: django check that produces an error for each badly named migration.

Add our check into your INSTALLED_APPS:

INSTALLED_APPS = [
    # ...

    # Our custom check:
    'django_test_migrations.contrib.django_checks.AutoNames',
]

Then in your CI run:

python manage.py check --deploy

This way you will be safe from wrong names in your migrations.

Do you have a migrations that cannot be renamed? Add them to the ignore list:

# settings.py

DTM_IGNORED_MIGRATIONS = {
    ('main_app', '0004_auto_20191119_2125'),
    ('dependency_app', '0001_auto_20201110_2100'),
}

Then we won't complain about them.

Or you can completely ignore entire app:

# settings.py

DTM_IGNORED_MIGRATIONS = {
    ('dependency_app', '*'),
    ('another_dependency_app', '*'),
}

Database configuration

Add our check to INSTALLED_APPS:

INSTALLED_APPS = [
    # ...

    # Our custom check:
    'django_test_migrations.contrib.django_checks.DatabaseConfiguration',
]

Then just run check management command in your CI like listed in section above.

Related projects

You might also like:

  • django-migration-linter - Detect backward incompatible migrations for your django project.
  • wemake-django-template - Bleeding edge django template focused on code quality and security with both django-test-migrations and django-migration-linter on board.

Credits

This project is based on work of other awesome people:

License

MIT.

django-test-migrations's People

Contributors

chel-ou avatar dependabot-preview[bot] avatar dependabot[bot] avatar dmytrolitvinov avatar elijahr avatar feuermurmel avatar github-actions[bot] avatar kytta avatar lerdem avatar maiksprenger avatar mijamo avatar skarzi avatar sobolevn avatar timgates42 avatar timur-orudzhov avatar wwarne avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-test-migrations's Issues

Add more DB configuration checks

In #91 we are introducing new Django check that validate system timeout settings on following database:

  • postgresql -statement_timeout
  • mysql - max_execution_time

The idea behind this group of checks is to help developers configure databases according to best practices.
If you have any ideas of such rules/checks, please shere in comments to this issue!

Some nice articles/sites about database configuration/settings:

properly reset databases on tests teardown

Currently multiple databases are supported by creating instance of Migrator for each database, but only default database is cleaned at the end of test - https://github.com/wemake-services/django-test-migrations/blob/master/django_test_migrations/migrator.py#L77

Also using migrate management command is not the best idea when it comes to performance, because there is no need to apply all unapplied migrations just before the next test, which should start with clean state.
Django's TestCase uses flush management command to clean databases after test.

We need to reuse Django's code responsible for cleaning database on test teardown (or establish our own way to do that) and fix Migrator.reset() method to clean Migrator.database instead of default one.

Django 3.2 compatibility

I know Django 3.2 is not officially supported, but I'm getting the following error importing MigratorTestCase with Django 3.2.0.
I'm on MacOS 10.15.6.

>>> env/bin/python
Python 3.7.0 (default, Oct  8 2020, 10:13:01)
[Clang 11.0.3 (clang-1103.0.32.62)] on darwin
>>> from django_test_migrations.contrib.unittest_case import MigratorTestCase
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "[repo_root]/env/lib/python3.7/site-packages/django_test_migrations/contrib/unittest_case.py", line 7, in <module>
    from django_test_migrations.migrator import Migrator
  File "[repo_root]/env/lib/python3.7/site-packages/django_test_migrations/migrator.py", line 9, in <module>
    from django_test_migrations import sql
  File "[repo_root]/env/lib/python3.7/site-packages/django_test_migrations/sql.py", line 5, in <module>
    from django.db import DefaultConnectionProxy, connections, transaction
ImportError: cannot import name 'DefaultConnectionProxy' from 'django.db' ([repo_root]/env/lib/python3.7/site-packages/django/db/__init__.py)

I've removed the absolute path for brevity.

The same error occurs on Django 3.2.1.
This works correctly with Django 3.1.9.

Is there any chance of this package being updated for compatibility with Django 3.2?

Thanks in advance for any help.

add possibility to run/skip only migrations tests

Calling tests with via pytest with --migrations or via Django's standard test runner (which apply migrations by default when using TransactionTestCase) can slow down tests a bit and when running tests in CI/CD pipeline we want to get feedback as fast as possible and often it's good idea to run standard and fast unit tests in parallel with migrations tests etc.

Currently it's really hard to run/skip only migrations tests and the easy solution is to add:

Confusing before/after methods.

These two methods do the same thing.

Why not call it migrate and reload the graph each time?

From my understanding before(('app', '0003')) should migrate to dependencies of 0003 but not the 0003 itself.

Upgrade dependecy of typing_extensions to ^4.0.0

Hello,

I would like to know if it's possible to upgrade the dependency to typing_extensions >= 4.0.0.

As of now we cannot introduce some external dependencies like pydantic in our project because of dependency conflicts with django-test-migrations. I don't exactly know if some breaking changes have been introduce in v4, however on the project README we can read the following:

Starting with version 4.0.0, typing_extensions uses Semantic Versioning. The major version is incremented for all backwards-incompatible changes, including dropping support for older Python versions. Therefore, it's safe to depend on typing_extensions like this: typing_extensions >=x.y, <(x+1), where x.y is the first version that includes all features you need.

Dependabot can't resolve your Python dependency files

Dependabot can't resolve your Python dependency files.

As a result, Dependabot couldn't update your dependencies.

The error Dependabot encountered was:

Creating virtualenv django-test-migrations-aTEqW9gF-py3.8 in /home/dependabot/.cache/pypoetry/virtualenvs
Updating dependencies
Resolving dependencies...

[PackageNotFound]
Package safety (1.8.5) not found.

If you think the above is an error on Dependabot's side please don't hesitate to get in touch - we'll do whatever we can to fix it.

View the update logs.

Extension 'ltree' already exists.

I wrote a test for the migration I am writing by hand, followed the guide for unittest and so on. Now, when I run pytest to run the actual test (this one alone) I get into the followin error:

E               django.db.utils.ProgrammingError: extension "ltree" already exists

I searched here as best as I could but could not find anything.

Worth mentioning we run our tests with pytest and we are using the pytest-django plugin to manage django setup and so on.

Thank you in advance for your help!

Migration signal muting occurs in the wrong place

The previous approach proposed in #11 uses a fixture to wrap the test which has the desired effect of muting signals before they are first emitted (pre_migrate) and un-muting them after the last emission (post_migrate).

The implementation in ab393b2 however performs the signal muting inside the transaction where it has no effect (Signals are muted after first emission and un-muted before last emission).

The following PoC/hack demonstrates a working approach master...GertBurger:move-signal-muting-to-fixture

I am not very familiar with pytest and its fixture ordering so the above approach may not be sufficient to ensure that the signal muting fixture always wraps the transaction fixture.

Dependabot can't resolve your Python dependency files

Dependabot can't resolve your Python dependency files.

As a result, Dependabot couldn't update your dependencies.

The error Dependabot encountered was:

Creating virtualenv django-test-migrations-BQFQEyOO-py3.8 in /home/dependabot/.cache/pypoetry/virtualenvs
Updating dependencies
Resolving dependencies...

[PackageNotFound]
Package safety (1.8.5) not found.

If you think the above is an error on Dependabot's side please don't hesitate to get in touch - we'll do whatever we can to fix it.

View the update logs.

Dependabot can't resolve your Python dependency files

Dependabot can't resolve your Python dependency files.

As a result, Dependabot couldn't update your dependencies.

The error Dependabot encountered was:

Creating virtualenv django-test-migrations-A4eaEADC-py3.8 in /home/dependabot/.cache/pypoetry/virtualenvs
Updating dependencies
Resolving dependencies...

[PackageNotFound]
Package safety (1.8.5) not found.

If you think the above is an error on Dependabot's side please don't hesitate to get in touch - we'll do whatever we can to fix it.

View the update logs.

Tests are skipped

Hi, I just wanted to give this project a go as I want to run a rather complex data migration.

However, as soon as I add the migrator fixture to my test case it's being skipped.

@pytest.mark.django_db
def test_datamig(migrator):
    pass

This is the test output I get:

Test session starts (platform: linux, Python 3.8.4, pytest 5.4.3, pytest-sugar 0.9.4)
cachedir: .pytest_cache
django: settings: xyz.settings (from option)
rootdir: /code, inifile: setup.cfg
plugins: django-test-migrations-1.0.0, xdist-1.34.0, celery-4.4.7, sugar-0.9.4, django-3.9.0, mock-3.3.0, Faker-4.1.2, socket-0.3.5, cov-2.10.1, forked-1.3.0
[gw0] linux Python 3.8.4 cwd: /code/sales_cockpit_be
[gw0] Python 3.8.4 (default, Jul 14 2020, 02:56:59)  -- [GCC 8.3.0]
gw0 [1]
scheduling tests via LoadScheduling

xyz/tests/test_migration.py::test_datamig s           100% ██████████
[gw0] SKIPPED xyz/tests/test_migration.py 

Results (14.02s):

       1 skipped

I also tried creating migrator manually inside the test with migrator = Migrator() or migrator_factory, but the issue persists.

I use the 1.0.0 version.

Does any1 have an idea what the reason could be?

use `pytester` to test our pytest plugin

django_test_migration is growing, so it's pytest plugin also, e.g. in #138 more logic was added to it.
Currently, most tests use subprocess to run pytest and make some assertions on its output.
The following approach is limited, it requires quite much code in each test and it runs pytest on all project's tests by default.
pytest has pytester plugin that is really useful for testing custom pytest plugins, so it will be nice to add more detailed tests of our django_test_migration plugin using pytester.
We could also try to remove pytest_plugin from the coverage:run omit option, because it should be covered by tests then.

Support `[email protected]`

Right now it fails:

poetry run pytest --nomigrations --cov-fail-under=0
Traceback (most recent call last):
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/pytest_django/plugin.py", line 179, in _handle_import_error
    yield
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/pytest_django/plugin.py", line 351, in pytest_load_initial_conftests
    dj_settings.DATABASES
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/django/conf/__init__.py", line 79, in __getattr__
    self._setup(name)
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/django/conf/__init__.py", line 66, in _setup
    self._wrapped = Settings(settings_module)
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/django/conf/__init__.py", line 157, in __init__
    mod = importlib.import_module(self.SETTINGS_MODULE)
  File "/opt/hostedtoolcache/Python/3.10.2/x64/lib/python3.10/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1004, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'django_test_app.settings'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/bin/pytest", line 8, in <module>
    sys.exit(console_main())
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/_pytest/config/__init__.py", line 188, in console_main
    code = main()
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/_pytest/config/__init__.py", line 146, in main
    config = _prepareconfig(args, plugins)
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/_pytest/config/__init__.py", line 325, in _prepareconfig
    config = pluginmanager.hook.pytest_cmdline_parse(
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/pluggy/_hooks.py", line 265, in __call__
    return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/pluggy/_manager.py", line 80, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/pluggy/_callers.py", line 55, in _multicall
    gen.send(outcome)
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/_pytest/helpconfig.py", line 102, in pytest_cmdline_parse
    config: Config = outcome.get_result()
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/pluggy/_result.py", line 60, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/pluggy/_callers.py", line 39, in _multicall
    res = hook_impl.function(*args)
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/_pytest/config/__init__.py", line 1013, in pytest_cmdline_parse
    self.parse(args)
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/_pytest/config/__init__.py", line 1301, in parse
    self._preparse(args, addopts=addopts)
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/_pytest/config/__init__.py", line 1203, in _preparse
    self.hook.pytest_load_initial_conftests(
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/pluggy/_hooks.py", line 265, in __call__
    return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/pluggy/_manager.py", line 80, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/pluggy/_callers.py", line 60, in _multicall
    return outcome.get_result()
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/pluggy/_result.py", line 60, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/pluggy/_callers.py", line 39, in _multicall
    res = hook_impl.function(*args)
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/pytest_django/plugin.py", line 350, in pytest_load_initial_conftests
    with _handle_import_error(_django_project_scan_outcome):
  File "/opt/hostedtoolcache/Python/3.10.2/x64/lib/python3.10/contextlib.py", line 153, in __exit__
    self.gen.throw(typ, value, traceback)
  File "/home/runner/work/django-test-migrations/django-test-migrations/.venv/lib/python3.10/site-packages/pytest_django/plugin.py", line 183, in _handle_import_error
    raise ImportError(msg)
ImportError: No module named 'django_test_app.settings'

pytest-django could not find a Django project (no manage.py file could be found). You must explicitly add your Django project to the Python path to have it picked up.
make: *** [Makefile:11: unit] Error 1

Link: https://github.com/wemake-services/django-test-migrations/runs/5087399817?check_suite_focus=true

Special key to ignore all migrations inside an app.

It would be useful to have special key for ignoring all migrations inside an app.

Consider case when we have some third party apps and want to ignore all migrations or for all third party apps at all.

Right now we should do something like this:

DTM_IGNORED_MIGRATIONS = [
    ('taggit', '0002_auto_20150616_2121'),
    ('two_factor', '0002_auto_20150110_0810'),
    ('two_factor', '0003_auto_20150817_1733'),
    ('two_factor', '0004_auto_20160205_1827'),
    ('two_factor', '0005_auto_20160224_0450'),
    ('waffle', '0002_auto_20161201_0958'),
]

With the special key, for example, it'll get much better:

THIRD_PARTY_APPS = (
    'taggit',
    'two_factor',
    'waffle',
)

# Apps specific for this project go here.
LOCAL_APPS = (
    'app_1',
    'app_2',
)

INSTALLED_APPS = THIRD_PARTY_APPS + LOCAL_APPS

DTM_IGNORED_MIGRATIONS = [
    (app, '*') for app in THIRD_PARTY_APPS
]

Import pre-migration data from an SQL file

Hi, is it possible to use an sql extract to test the migration procedure? ie

class TestDirectMigration(MigratorTestCase,_TestUtils):
    """This class is used to test direct migrations."""
    migrate_from = ('app', '0016_auto_20200416_1527')
    migrate_to = ('app', '0017_auto_20200511_1641')

    def prepare(self):
        """Prepare some data before the migration."""
        test_data = self.resource_path('migration_test_data/0016_auto_20200416_1527.sql')
        with connection.cursor() as cursor:
            for l in test_data.read_text().strip().split(';'):
                cursor.execute(l)

Dependabot can't resolve your Python dependency files

Dependabot can't resolve your Python dependency files.

As a result, Dependabot couldn't update your dependencies.

The error Dependabot encountered was:

Creating virtualenv django-test-migrations-8iGcF5E9-py3.8 in /home/dependabot/.cache/pypoetry/virtualenvs
Updating dependencies
Resolving dependencies...

[PackageNotFound]
Package pytest-pythonpath (0.7.3) not found.

If you think the above is an error on Dependabot's side please don't hesitate to get in touch - we'll do whatever we can to fix it.

View the update logs.

Dependabot can't resolve your Python dependency files

Dependabot can't resolve your Python dependency files.

As a result, Dependabot couldn't update your dependencies.

The error Dependabot encountered was:

Creating virtualenv django-test-migrations-Q_RgL_FZ-py3.8 in /home/dependabot/.cache/pypoetry/virtualenvs
Updating dependencies
Resolving dependencies...

[PackageNotFound]
Package safety (1.8.5) not found.

If you think the above is an error on Dependabot's side please don't hesitate to get in touch - we'll do whatever we can to fix it.

View the update logs.

typing_extensions is still required in newer Python versions

This line is in poetry.lock

[package.dependencies]
attrs = ">=20"
typing_extensions = {version = "*", markers = "python_version >= \"3.7\" and python_version < \"3.8\""}

However, on a fresh Ubuntu 20.04 system, after I install pytest and pytest-django, I got this error when I ran my tests:

traceback snipped
  File "/home/vagrant/.local/share/virtualenvs/redacted-ikMrF094/lib/python3.8/site-packages/_pytest/assertion/rewrite.py", line 170, in exec_module
    exec(co, module.__dict__)
  File "/home/vagrant/.local/share/virtualenvs/redacted/lib/python3.8/site-packages/django_test_migrations/contrib/pytest_plugin.py", line 6, in <module>
    from django_test_migrations.constants import MIGRATION_TEST_MARKER
  File "/home/vagrant/.local/share/virtualenvs/redacted/lib/python3.8/site-packages/_pytest/assertion/rewrite.py", line 170, in exec_module
    exec(co, module.__dict__)
  File "/home/vagrant/.local/share/virtualenvs/redacted/lib/python3.8/site-packages/django_test_migrations/constants.py", line 1, in <module>
    from typing_extensions import Final
ModuleNotFoundError: No module named 'typing_extensions'

It would appear this module still needs to depend on typing_extensions.

Ubuntu 20.04

Python 3.8.6

Create a check for initial migrations

Today I found a code like this:

class Migration(migrations.Migration):

    # initial = True
    dependencies = []

And this was an initial migration for an app.

This suprised me:

  1. It works correctly
  2. It does not produce any warnings
  3. It is cryptic

What am I concerned about? I have an impression that all migrations with dependencies = [] must be initial, because otherwise they should be dependent on a previous migration. Am I correct?

So, I guess we can check this pretty easily in case initial really works this way.

`migrator` fixture clashes with `db` fixture

I am preloading some test data via a migration, and once my sole migration test has run, it wipes that data out and other tests fail the next time i do a test run.

The culprit is

@pytest.fixture()
def migrator_factory(request, transactional_db, django_db_use_migrations):

If I remove transactional_db then tests pass again

My conftest looks like this:

@pytest.fixture(autouse=True)
def enable_db_access_for_all_tests(db):
    pass


@pytest.fixture()
def migrator():
    migrator = Migrator(database='default')
    yield migrator
    migrator.reset()

My custom migrator fixture gets tests passing again.

Models from app state aren't compatible (Cannot assign "<A: A object (1)>": "B.a_fk" must be a "A" instance)

I'm running into a problem when I apply a migration with Migrator.apply_initial_migration() and then try to construct model instances using the returned app state. One model has a foreign key to another model, but the foreign key field does not accept an instance of that model (see below for the exact output and exception).

I tried to reduce the setup as much a possible. In the end, two additional models and 2 indexes were necessary to trigger the issue. I tested it with django-test-migrations 1.2.0 and with Django 3.2.13 and 4.0.4. The attached ZIP archive contains a complete project which reproduces the issue:

app-state-bug.zip

To run the test, use the following couple of commands:

python3.10 -m venv venv
venv/bin/pip install django~=3.2 pytest-django django-test-migrations
venv/bin/pytest test_bad.py

When running this, you should see the following output:

___________________________________ test_bad ___________________________________

migrator = <django_test_migrations.migrator.Migrator object at 0x105c44430>

    def test_bad(migrator):
        state = migrator.apply_initial_migration(('my_app', '0002_foo'))
    
        A = state.apps.get_model('my_app', 'A')
        B = state.apps.get_model('my_app', 'B')
    
        print(id(A), id(B.a_fk.field.related_model))
    
>       B.objects.create(a_fk=A.objects.create(foo=1))

test_bad.py:9: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
venv/lib/python3.10/site-packages/django/db/models/manager.py:85: in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
venv/lib/python3.10/site-packages/django/db/models/query.py:512: in create
    obj = self.model(**kwargs)
venv/lib/python3.10/site-packages/django/db/models/base.py:541: in __init__
    _setattr(self, field.name, rel_obj)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django.db.models.fields.related_descriptors.ForwardManyToOneDescriptor object at 0x105c47520>
instance = <B: B object (None)>, value = <A: A object (1)>

    def __set__(self, instance, value):
        """
        Set the related instance through the forward relation.
    
        With the example above, when setting ``child.parent = parent``:
    
        - ``self`` is the descriptor managing the ``parent`` attribute
        - ``instance`` is the ``child`` instance
        - ``value`` is the ``parent`` instance on the right of the equal sign
        """
        # An object must be an instance of the related class.
        if value is not None and not isinstance(
            value, self.field.remote_field.model._meta.concrete_model
        ):
>           raise ValueError(
                'Cannot assign "%r": "%s.%s" must be a "%s" instance.'
                % (
                    value,
                    instance._meta.object_name,
                    self.field.name,
                    self.field.remote_field.model._meta.object_name,
                )
E               ValueError: Cannot assign "<A: A object (1)>": "B.a_fk" must be a "A" instance.

venv/lib/python3.10/site-packages/django/db/models/fields/related_descriptors.py:235: ValueError

Tests with MigratorTestCase cause failures in other tests

Hi, I'm working on a medium-sized Django codebase and recently introduced django-test-migrations to test our migrations. It's a great project and solves a need that many serious Django apps have. We'd like to see it improved by fixing a blocking issue we're seeing.

Unfortunately, when our test suite isn't ordered in the usual fashion, I'm seeing failures. This is true for random ordering, and predictable but non-standard ordering (see below).

I'm careful to draw conclusions, but to me it seems likely that the issue is with our use of django-test-migrations. It doesn't always appear, but it only appears when those tests are run.

I'm a bit at loss about what might be causing the issue, and don't know how to debug further. I do have some work-time available to help debug this issue. I'd be interested to know if you've heard of this issue before. And please let me know how I can assist.

Things I did

  • Ran test suite without new tests in random order multiple times: no failures
  • Ran test suite with new tests in random order: failures almost every time so far
  • Ran test suite with new tests in standard order: no failures
  • Ran test suite with new tests in parallel on CircleCI: failures
  • Ran test suite with new tests and clear ContentType cache before and after each test: failures. I did this because it was documented as possible solution here. Also see #10827.

Offending tests

The two tests that seem to be causing the unrelated failures are here: https://gist.github.com/maikhoepfel/0c4c52c86f047c79b85d8b4e98af194f I don't think they do anything special.

The tests I added aren't the first tests for migrations. We've been employing something very similar to the blog post where it all started. Only when we added django-test-migrations did we see the issue. This is surprising to me, because the actual code executed is similar.

The failures

django.db.utils.IntegrityError: insert or update on table "core_stock" violates foreign key constraint "core_stock_content_type_id_d8f48c0c_fk_django_content_type_id" DETAIL:  Key (content_type_id)=(1) is not present in table "django_content_type".
self = <django.db.backends.utils.CursorWrapper object at 0x7f4b5f881dd8>
sql = 'SET CONSTRAINTS ALL IMMEDIATE', params = None
ignored_wrapper_args = (False, {'connection': <django.db.backends.postgresql.base.DatabaseWrapper object at 0x7f4b675bd4a8>, 'cursor': <django.db.backends.utils.CursorWrapper object at 0x7f4b5f881dd8>})

    def _execute(self, sql, params, *ignored_wrapper_args):
        self.db.validate_no_broken_transaction()
        with self.db.wrap_database_errors:
            if params is None:
>               return self.cursor.execute(sql)
E               psycopg2.errors.ForeignKeyViolation: insert or update on table "core_stock" violates foreign key constraint "core_stock_content_type_id_d8f48c0c_fk_django_content_type_id"
E               DETAIL:  Key (content_type_id)=(1) is not present in table "django_content_type".

venv/lib/python3.6/site-packages/django/db/backends/utils.py:82: ForeignKeyViolation

The above exception was the direct cause of the following exception:

self = <tests.ondemand.views.test_views.StoreCartTestCase testMethod=test_checkout_cart_pricing_rule_limit_add_occurences>
result = <TestCaseFunction test_checkout_cart_pricing_rule_limit_add_occurences>

    def __call__(self, result=None):
        """
        Wrapper around default __call__ method to perform common Django test
        set up. This means that user-defined Test Cases aren't required to
        include a call to super().setUp().
        """
        testMethod = getattr(self, self._testMethodName)
        skipped = (
            getattr(self.__class__, "__unittest_skip__", False) or
            getattr(testMethod, "__unittest_skip__", False)
        )
    
        if not skipped:
            try:
                self._pre_setup()
            except Exception:
                result.addError(self, sys.exc_info())
                return
        super().__call__(result)
        if not skipped:
            try:
>               self._post_teardown()

venv/lib/python3.6/site-packages/django/test/testcases.py:274: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
venv/lib/python3.6/site-packages/django/test/testcases.py:1009: in _post_teardown
    self._fixture_teardown()
venv/lib/python3.6/site-packages/django/test/testcases.py:1177: in _fixture_teardown
    connections[db_name].check_constraints()
venv/lib/python3.6/site-packages/django/db/backends/postgresql/base.py:246: in check_constraints
    self.cursor().execute('SET CONSTRAINTS ALL IMMEDIATE')
venv/lib/python3.6/site-packages/sentry_sdk/integrations/django/__init__.py:487: in execute
    return real_execute(self, sql, params)
venv/lib/python3.6/site-packages/django/db/backends/utils.py:67: in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
venv/lib/python3.6/site-packages/django/db/backends/utils.py:76: in _execute_with_wrappers
    return executor(sql, params, many, context)
venv/lib/python3.6/site-packages/django/db/backends/utils.py:82: in _execute
    return self.cursor.execute(sql)
venv/lib/python3.6/site-packages/django/db/utils.py:89: in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django.db.backends.utils.CursorWrapper object at 0x7f4b5f881dd8>
sql = 'SET CONSTRAINTS ALL IMMEDIATE', params = None
ignored_wrapper_args = (False, {'connection': <django.db.backends.postgresql.base.DatabaseWrapper object at 0x7f4b675bd4a8>, 'cursor': <django.db.backends.utils.CursorWrapper object at 0x7f4b5f881dd8>})

    def _execute(self, sql, params, *ignored_wrapper_args):
        self.db.validate_no_broken_transaction()
        with self.db.wrap_database_errors:
            if params is None:
>               return self.cursor.execute(sql)
E               django.db.utils.IntegrityError: insert or update on table "core_stock" violates foreign key constraint "core_stock_content_type_id_d8f48c0c_fk_django_content_type_id"
E               DETAIL:  Key (content_type_id)=(1) is not present in table "django_content_type".

venv/lib/python3.6/site-packages/django/db/backends/utils.py:82: IntegrityError

other `django-test-migrations`

Hello 👋
last year I have encountered problems with Django’s migrations similar to these described by you on your blog and after that, I also have decided to create a library to test that kind of code properly - http://github.com/skarzi/django-test-migrations.
We were both inspired by the same articles/code snippets so our code is almost identical.

I wanted to release my code after some polishing and refactoring, but you did it first. I think we could start working together on one project t's always more profitable for the community. Below I list some issues me and other developers (it has been used in several side and commercial projects) have spotted in my implementation:

  • multiple DB support - in your implementation it's possible but requires creating few instances of migrator
  • properly run initial migrations - currently, it runs all migrations forward (it's done either by Django’s test case or pytest's --migrations cli option) and then migrations are reverted to these specified in .before(). Such an approach can cause a really ugly tracebacks.
  • properly clean DB after tests - currently migrate command is called but it's run only on default DB (check Django’s test case source code for proper solution - https://github.com/django/django/blob/master/django/test/testcases.py#L1015)
  • detection and update of settings.MIGRATION_MODULES for migration tests

I also wanted to add intergration with factory_boy - creating models' instances manually (with ModelClass.objects.create() or ModelClass.objects.bulk_create()) is really tiresome, but I need to do more experiments to decide on the best API for that.

apply_initial_migration fails

Hi,

I've just discovered this package and wanted to test a new migration that I am writing. So far, the project consists of a few apps, and the app I want to write migration tests for has currently > 100 applied migrations.
For the sake of simplicity, I set migrate_from and migrate_to to an already applied migration (~101 -> 102, only a change in a field type) just to fiddle arround. Curiously, it failed.

Traceback:

`MySQLdb._exceptions.OperationalError: (3730, "Cannot drop table 'wagtailcore_collectionviewrestriction' referenced by a foreign key constraint 'wagtailcore_collecti_collectionviewrestri_47320efd_fk_wagtailco' on table 'wagtailcore_collectionviewrestriction_groups'.")

env/lib/python3.6/site-packages/MySQLdb/connections.py:239: OperationalError

The above exception was the direct cause of the following exception:
env/lib/python3.6/site-packages/django_test_migrations/contrib/unittest_case.py:36: in setUp
    self.migrate_from,
env/lib/python3.6/site-packages/django_test_migrations/migrator.py:46: in apply_initial_migration
    sql.drop_models_tables(self._database, style)
env/lib/python3.6/site-packages/django_test_migrations/sql.py:32: in drop_models_tables
    get_execute_sql_flush_for(connection)(database_name, sql_drop_tables)
env/lib/python3.6/site-packages/django/db/backends/base/operations.py:405: in execute_sql_flush
    cursor.execute(sql)
env/lib/python3.6/site-packages/django/db/backends/utils.py:68: in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
env/lib/python3.6/site-packages/django/db/backends/utils.py:77: in _execute_with_wrappers
    return executor(sql, params, many, context)
env/lib/python3.6/site-packages/django/db/backends/utils.py:86: in _execute
    return self.cursor.execute(sql, params)
env/lib/python3.6/site-packages/django/db/utils.py:90: in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
env/lib/python3.6/site-packages/django/db/backends/utils.py:84: in _execute
    return self.cursor.execute(sql)
env/lib/python3.6/site-packages/django/db/backends/mysql/base.py:74: in execute
    return self.cursor.execute(query, args)
env/lib/python3.6/site-packages/MySQLdb/cursors.py:209: in execute
    res = self._query(query)
env/lib/python3.6/site-packages/MySQLdb/cursors.py:315: in _query
    db.query(q)

and a lot more other errors related to drop queries. As I understand it, during setup, django-test-migrations takes the current test_db and deletes all models in the database, and then reapplies all migrations up and including to migrate_from. In my case, this seems to fail. I cannot imagine what I could have done wrong in my testcase, as the error occurs during the setUp function of the MigratorTestCase.

I don't think that it has anything to do with wagtail, as the testcase seems to fail with different errors on each run:

self = <_mysql.connection closed at 0x35419e8>
query = b'DROP TABLE `auth_group` CASCADE'

    def query(self, query):
        # Since _mysql releases GIL while querying, we need immutable buffer.
        if isinstance(query, bytearray):
            query = bytes(query)
>       _mysql.connection.query(self, query)
E       django.db.utils.OperationalError: (3730, "Cannot drop table 'auth_group' referenced by a foreign key constraint 'auth_group_permissions_group_id_b120cbf9_fk_auth_group_id' on table 'auth_group_permissions'.")

Testcase:

class TestMigrations(MigratorTestCase):
    migrate_from = ('myapp', None)
    migrate_to = ('myapp', '001_initial')

    def prepare(self):
        pass

    def test_migration001(self):
        self.assertTrue(True)

Maybe I am missing something crucial and obvious, so here is what I did:

install django-test-migrations with pip
wrote a unittest testcase, as provided in the example (the actual tc is just an assertTrue(True)
ran manage.py test
I did not add anything to INSTALLED_APPS whatsoever.

Any ideas?

DB: MYSQL 8.0.19 mysqlclient (1.4.6)
Django: 3.0.2
django-test-migrations: 1.0.0

Dependabot can't resolve your Python dependency files

Dependabot can't resolve your Python dependency files.

As a result, Dependabot couldn't update your dependencies.

The error Dependabot encountered was:

Creating virtualenv django-test-migrations-OjLT_eJI-py3.8 in /home/dependabot/.cache/pypoetry/virtualenvs
Updating dependencies
Resolving dependencies...

[PackageNotFound]
Package safety (1.8.5) not found.

If you think the above is an error on Dependabot's side please don't hesitate to get in touch - we'll do whatever we can to fix it.

View the update logs.

integration with factory-boy or other similar solution

Many of existing Django projects use factory-boy or other similar solution to create models instances before actual tests, so it will be really handy to provide some integration with such tools to make migrations testing easier and less verbose than Model.objects.create() etc.

properly apply migrations before running test

Current django-test-migrations tests setup runs all migrations forward (it's done either by Django’s test case or pytest's --migrations cli option) and then migrations are reverted by .before() to these specified in its arguments. Such an approach can cause a really ugly and unintuitive tracebacks.

Proper solution will be defer migrations running until .before() is called.
This way we can avoid applying unnecessary migrations' operations that will be reverted anyway in .before() and migrations's exceptions will be raised only when particular migration will be tested (currently if any migration even the last one in the plan is incorrect and will raise some exception then all migrations tests will be interrupted by this exception which will produce really unintuituve tracebacks).

If anyone has some other ideas or is against it, please raise your hand, so we can figure out the best solution for this issue.

Can't handle squashed migrations

My migration is doing something like the following:

Note: Parent and Child are both subclasses of Activity, and there is a generic relation between them (not sure if this is related to the issues I'm seeing)

@pytest.mark.django_db
def test_activity_migration(migrator: Migrator):
    old_state = migrator.apply_tested_migration(("app", an_old_migration_name))
    Parent = old_state.apps.get_model("app", "Parent")
    Child = old_state.apps.get_model("app", "Child")

    # Data setup
    parent = Parent.objects.create(...)
    Child.objects.create(..., parent=parent)
    orig_parents_values = list(Parent.objects.values())
    orig_children_values = list(Child.objects.values())

    # Finish all migrations on app
    state_0007 = migrator.apply_tested_migration(("app", the_newer_migration_name))
    state_0007_Parent = state_0007.apps.get_model("app", "Parent")
    state_0007_Child = state_0007.apps.get_model("app", "Child")
    state_0007_Activity = state_0007.apps.get_model("app", "Activity")
    assert models.Activity.objects.count() == 2

    # do not make assertion for the parent_activity_id
    orig_children_values[0].pop("parent_id")

    parent_0007 = state_0007_Parent.objects.values().first()
    child_0007 = state_0007_Child.objects.values().first()

    assert (
        state_0007_Activity.objects.filter(id=parent["activity_ptr_id"])
        .values(*orig_parents_values[0].keys())
        .first()
        == orig_parents_values[0]
    )
    assert (
        state_0007_Activity.objects.filter(id=childActivity["activity_ptr_id"])
        .values(*orig_children_values[0].keys())
        .first()
        == orig_children_values[0]
    )

After squashing 2 unrelated migrations together (using manage.py squashmigrations, this test no longer runs. It gives me this traceback:

______________________________________________________________ ERROR at teardown of test_activity_migration ______________________________________________________________
[snipped]

/home/joel/.cache/pypoetry/virtualenvs/relate-en3S9GuS-py3.8/lib/python3.8/site-packages/django_test_migrations/migrator.py:76: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/home/joel/.cache/pypoetry/virtualenvs/relate-en3S9GuS-py3.8/lib/python3.8/site-packages/django/core/management/__init__.py:181: in call_command
    return command.execute(*args, **defaults)
/home/joel/.cache/pypoetry/virtualenvs/relate-en3S9GuS-py3.8/lib/python3.8/site-packages/django/core/management/base.py:398: in execute
    output = self.handle(*args, **options)
/home/joel/.cache/pypoetry/virtualenvs/relate-en3S9GuS-py3.8/lib/python3.8/site-packages/django/core/management/base.py:89: in wrapped
    res = handle_func(*args, **kwargs)
/home/joel/.cache/pypoetry/virtualenvs/relate-en3S9GuS-py3.8/lib/python3.8/site-packages/django/core/management/commands/migrate.py:95: in handle
    executor.loader.check_consistent_history(connection)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

[snipped]
E                   django.db.migrations.exceptions.InconsistentMigrationHistory: Migration app.0015_squashed_migration_name is applied before its dependency app.0014_previous_migration_name on database 'default'.

/home/joel/.cache/pypoetry/virtualenvs/relate-en3S9GuS-py3.8/lib/python3.8/site-packages/django/db/migrations/loader.py:306: InconsistentMigrationHistory
___________________________________________________________ ERROR at teardown of test_automatic_child_activity ___________________________________________________________

[snipped]

/home/joel/.cache/pypoetry/virtualenvs/relate-en3S9GuS-py3.8/lib/python3.8/site-packages/django_test_migrations/migrator.py:76: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/home/joel/.cache/pypoetry/virtualenvs/relate-en3S9GuS-py3.8/lib/python3.8/site-packages/django/core/management/__init__.py:181: in call_command
    return command.execute(*args, **defaults)
/home/joel/.cache/pypoetry/virtualenvs/relate-en3S9GuS-py3.8/lib/python3.8/site-packages/django/core/management/base.py:398: in execute
    output = self.handle(*args, **options)
/home/joel/.cache/pypoetry/virtualenvs/relate-en3S9GuS-py3.8/lib/python3.8/site-packages/django/core/management/base.py:89: in wrapped
    res = handle_func(*args, **kwargs)
/home/joel/.cache/pypoetry/virtualenvs/relate-en3S9GuS-py3.8/lib/python3.8/site-packages/django/core/management/commands/migrate.py:95: in handle
    executor.loader.check_consistent_history(connection)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django.db.migrations.loader.MigrationLoader object at 0x7f808bbd0fd0>, connection = <django.db.backends.postgresql.base.DatabaseWrapper object at 0x7f8091eaaa30>

[snipped]
E                   django.db.migrations.exceptions.InconsistentMigrationHistory: Migration app.0015_squashed_migration_name is applied before its dependency app.0014_previous_migration_name on database 'default'.

/home/joel/.cache/pypoetry/virtualenvs/relate-en3S9GuS-py3.8/lib/python3.8/site-packages/django/db/migrations/loader.py:306: InconsistentMigrationHistory

After this there is a second failure on the same test, related to the migrations not having applied successfully.

Post migrate signal receiver of the auth contrib gives me ForeignKeyViolation.

Error message

____________________________________________________ ERROR at teardown of test _____________________________________________________

self = <django.db.backends.postgresql.base.DatabaseWrapper object at 0x7f8187ac3588>

    def _commit(self):
        if self.connection is not None:
            with self.wrap_database_errors:
>               return self.connection.commit()
E               psycopg2.errors.ForeignKeyViolation: insert or update on table "auth_permission" violates foreign key constraint "auth_permission_content_type_id_2f476e4b_fk_django_co"
E               DETAIL:  Key (content_type_id)=(385) is not present in table "django_content_type".

../../.pyenv/versions/_/lib/python3.7/site-packages/django/db/backends/base/base.py:236: ForeignKeyViolation

The above exception was the direct cause of the following exception:

self = <django.test.testcases.TransactionTestCase testMethod=__init__>

    def _post_teardown(self):
        """Performs any post-test things. This includes:

        * Flushing the contents of the database, to leave a clean slate. If
          the class has an 'available_apps' attribute, post_migrate isn't fired.
        * Force-closing the connection, so the next test gets a clean cursor.
        """
        try:
>           self._fixture_teardown()

../../.pyenv/versions/_/lib/python3.7/site-packages/django/test/testcases.py:925:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../.pyenv/versions/_/lib/python3.7/site-packages/django/test/testcases.py:960: in _fixture_teardown
    inhibit_post_migrate=inhibit_post_migrate)
../../.pyenv/versions/_/lib/python3.7/site-packages/django/core/management/__init__.py:131: in call_command
    return command.execute(*args, **defaults)
../../.pyenv/versions/_/lib/python3.7/site-packages/django/core/management/base.py:330: in execute
    output = self.handle(*args, **options)
../../.pyenv/versions/_/lib/python3.7/site-packages/django/core/management/commands/flush.py:88: in handle
    emit_post_migrate_signal(verbosity, interactive, database)
../../.pyenv/versions/_/lib/python3.7/site-packages/django/core/management/sql.py:53: in emit_post_migrate_signal
    **kwargs
../../.pyenv/versions/_/lib/python3.7/site-packages/django/dispatch/dispatcher.py:193: in send
    for receiver in self._live_receivers(sender)
../../.pyenv/versions/_/lib/python3.7/site-packages/django/dispatch/dispatcher.py:193: in <listcomp>
    for receiver in self._live_receivers(sender)
../../.pyenv/versions/_/lib/python3.7/site-packages/django/contrib/auth/management/__init__.py:83: in create_permissions
    Permission.objects.using(using).bulk_create(perms)
../../.pyenv/versions/_/lib/python3.7/site-packages/django/db/models/query.py:449: in bulk_create
    obj_without_pk._state.db = self.db
../../.pyenv/versions/_/lib/python3.7/site-packages/django/db/transaction.py:223: in __exit__
    connection.commit()
../../.pyenv/versions/_/lib/python3.7/site-packages/django/db/backends/base/base.py:262: in commit
    self._commit()
../../.pyenv/versions/_/lib/python3.7/site-packages/django/db/backends/base/base.py:236: in _commit
    return self.connection.commit()
../../.pyenv/versions/_/lib/python3.7/site-packages/django/db/utils.py:94: in __exit__
    six.reraise(dj_exc_type, dj_exc_value, traceback)
../../.pyenv/versions/_/lib/python3.7/site-packages/django/utils/six.py:685: in reraise
    raise value.with_traceback(tb)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <django.db.backends.postgresql.base.DatabaseWrapper object at 0x7f8187ac3588>

    def _commit(self):
        if self.connection is not None:
            with self.wrap_database_errors:
>               return self.connection.commit()
E               django.db.utils.IntegrityError: insert or update on table "auth_permission" violates foreign key constraint "auth_permission_content_type_id_2f476e4b_fk_django_co"
E               DETAIL:  Key (content_type_id)=(385) is not present in table "django_content_type".

../../.pyenv/versions/_/lib/python3.7/site-packages/django/db/backends/base/base.py:236: IntegrityError

Workaround

@pytest.fixture(autouse=True)
def _mute_post_migrate_signal():
    from django.db.models.signals import post_migrate
    restore, post_migrate.receivers = post_migrate.receivers, []
    yield
    post_migrate.receivers = restore

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.