{"id":5912,"library":"django-test-migrations","title":"Django Test Migrations","description":"django-test-migrations is a Python library designed for robustly testing Django schema and data migrations, ensuring they apply correctly, roll back safely, and maintain data integrity. It supports testing migration order and names, and works across multiple Django versions (currently 3.2, 4.1, 4.2, 5.0, 5.2). The library is actively maintained with regular updates for Python and Django version compatibility. The current version is 1.5.0, released on April 18, 2025.","status":"active","version":"1.5.0","language":"en","source_language":"en","source_url":"https://github.com/wemake-services/django-test-migrations","tags":["django","testing","migrations","data-migrations","schema-migrations","database","test-utilities"],"install":[{"cmd":"pip install django-test-migrations","lang":"bash","label":"Install latest version"}],"dependencies":[{"reason":"Core framework dependency; supports Django versions 3.2, 4.1, 4.2, 5.0, 5.2.","package":"Django","optional":false},{"reason":"Requires Python versions >=3.10, <4.0.","package":"Python","optional":false},{"reason":"Provides backported and experimental type hints.","package":"typing-extensions","optional":false}],"imports":[{"note":"Used for programmatic migration testing, especially with pytest.","symbol":"Migrator","correct":"from django_test_migrations.migrator import Migrator"},{"note":"MigrationTest is from an older/related project's common import path (django_migration_testcase), while Migrator is the primary class for the wemake-services library. Ensure you're importing the correct class based on which API you intend to use.","wrong":"from django_test_migrations.migrator import MigrationTest","symbol":"MigrationTest","correct":"from django_migration_testcase import MigrationTest"}],"quickstart":{"code":"import os\nimport pytest\nfrom django.conf import settings\n\n# Minimal Django settings for testing if not already configured\n# In a real project, this would typically be in your settings.py\nif not settings.configured:\n    settings.configure(\n        INSTALLED_APPS=[\n            'django.contrib.admin',\n            'django.contrib.auth',\n            'django.contrib.contenttypes',\n            'django.contrib.sessions',\n            'django.contrib.messages',\n            'django.contrib.staticfiles',\n            # Add your app here, e.g., 'myapp'\n        ],\n        DATABASES={\n            'default': {\n                'ENGINE': 'django.db.backends.sqlite3',\n                'NAME': ':memory:',\n            }\n        },\n        MIGRATION_MODULES={ # Example for a hypothetical app 'myapp'\n            # 'myapp': 'myapp.migrations' # Uncomment if your app has migrations\n        }\n    )\n\nfrom django_test_migrations.migrator import Migrator\n\n@pytest.fixture(scope='function')\ndef migrator(db) -> Migrator:\n    \"\"\"Provides a Migrator instance for testing against the 'default' database.\"\"\"\n    return Migrator(database='default')\n\ndef test_my_migration(migrator: Migrator):\n    # Set up the database to a state *before* the migration you want to test\n    # Example: app_label 'myapp', migration name '0001_initial'\n    old_state = migrator.apply_initial_migration(('myapp', '0001_initial'))\n\n    # Get the model class from the old state\n    # Replace 'MyModel' with your actual model name\n    OldMyModel = old_state.apps.get_model('myapp', 'MyModel')\n\n    # Create some data that will be affected by the migration\n    # Example: creating an instance with an old field value\n    old_instance = OldMyModel.objects.create(some_old_field='value')\n\n    # Apply the migration you want to test\n    # Example: app_label 'myapp', migration name '0002_new_field'\n    new_state = migrator.apply_tested_migration(('myapp', '0002_new_field'))\n\n    # Get the model class from the new state\n    NewMyModel = new_state.apps.get_model('myapp', 'MyModel')\n\n    # Assertions: Check that the data was migrated correctly\n    new_instance = NewMyModel.objects.get(pk=old_instance.pk)\n    assert hasattr(new_instance, 'some_new_field') # Check for new field\n    # assert new_instance.some_new_field == 'expected_new_value'\n\n    # Optional: Test rollback\n    # migrator.reset()\n    # assert not NewMyModel.objects.filter(pk=old_instance.pk).exists() # Or check rolled back state\n","lang":"python","description":"The quickstart demonstrates using the `Migrator` class with `pytest` to test a specific migration. It outlines how to set up the database to an 'initial' state before the migration, create test data using the 'old' model state, apply the migration, and then assert the results using the 'new' model state. It emphasizes accessing models through `old_state.apps.get_model()` and `new_state.apps.get_model()` to ensure correct historical model versions are used."},"warnings":[{"fix":"Update calls from `migrator.before()` to `migrator.apply_initial_migration()` and `migrator.after()` to `migrator.apply_tested_migration()`.","message":"In version 1.0.0, the `Migrator` methods `before` and `after` were renamed to `apply_initial_migration` and `apply_tested_migration` respectively. Code using the old method names will break.","severity":"breaking","affected_versions":">=1.0.0"},{"fix":"Always retrieve models using the provided state objects: `old_state.apps.get_model('myapp', 'MyModel')` or `new_state.apps.get_model('myapp', 'MyModel')`. This ensures you're interacting with the correct model schema at each stage of the migration test.","message":"Directly importing models into your migration tests (e.g., `from myapp.models import MyModel`) is a common pitfall. This can lead to unexpected behavior or failures as your tests won't be using the historical model state relevant to the specific migration being tested.","severity":"gotcha","affected_versions":"All versions"},{"fix":"For multiple apps, define `before = [('app1', '0001'), ('app2', '0001')]` and `after = [('app1', '0002'), ('app2', '0002')]`. Use `self.get_model_before('app_name.MyModel')` to get models.","message":"When testing multiple Django applications that have interdependent migrations, you must provide `before` and `after` as a list of `(app_name, migration_name)` tuples. Additionally, when retrieving models, explicitly specify the app name (e.g., `self.get_model_before('otherapp.OtherModel')`).","severity":"gotcha","affected_versions":"All versions"},{"fix":"If you need to test your own `post_migrate` signals, attach and remove them explicitly within your test methods or setup/teardown.","message":"If your tests involve Django's `post_migrate` signals, be aware that `django-test-migrations` clears the receiver list at the start of tests and restores it afterwards. This is to prevent side effects.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Upgrade your project's Python version to 3.10 or newer, and Django version to 3.2, 4.1, 4.2, 5.0, or 5.2.","message":"Support for older Python and Django versions has been dropped in recent releases. Specifically, Python 3.7, 3.8, and 3.9, and Django 2.2 are no longer officially supported.","severity":"deprecated","affected_versions":">=1.3.0 (Python 3.7, Django 2.2), >=1.4.0 (Python 3.8), >=1.5.0 (Python 3.9)"}],"env_vars":null,"last_verified":"2026-04-14T00:00:00.000Z","next_check":"2026-07-13T00:00:00.000Z"}