{"id":16750338,"url":"https://github.com/plumdog/django_migration_testcase","last_synced_at":"2025-08-11T09:07:57.174Z","repository":{"id":29294063,"uuid":"32826970","full_name":"plumdog/django_migration_testcase","owner":"plumdog","description":"For testing migrations in Django","archived":false,"fork":false,"pushed_at":"2020-03-17T00:43:41.000Z","size":75,"stargazers_count":42,"open_issues_count":8,"forks_count":7,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-06-09T05:44:38.412Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/plumdog.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-03-24T21:44:14.000Z","updated_at":"2025-05-18T02:22:52.000Z","dependencies_parsed_at":"2022-08-07T14:16:02.896Z","dependency_job_id":null,"html_url":"https://github.com/plumdog/django_migration_testcase","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/plumdog/django_migration_testcase","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plumdog%2Fdjango_migration_testcase","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plumdog%2Fdjango_migration_testcase/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plumdog%2Fdjango_migration_testcase/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plumdog%2Fdjango_migration_testcase/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/plumdog","download_url":"https://codeload.github.com/plumdog/django_migration_testcase/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plumdog%2Fdjango_migration_testcase/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269857469,"owners_count":24486392,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-11T02:00:10.019Z","response_time":75,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-13T02:27:50.508Z","updated_at":"2025-08-11T09:07:57.143Z","avatar_url":"https://github.com/plumdog.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# django_migration_testcase\n[![Build Status](https://travis-ci.org/plumdog/django_migration_testcase.svg?branch=master)](https://travis-ci.org/plumdog/django_migration_testcase)\n\nFor testing migrations in Django \u003e= 1.4 (both South and Django migrations)\n\nBecause migrations are important. And if they go wrong, people get\nangry. How better to be sure that they won't go wrong than to run\ntests.\n\nI found [this article](https://micknelson.wordpress.com/2013/03/01/testing-django-migrations/)\non writing tests around South migrations, which I had used, but as of\nDjango 1.7, I was out of luck. So I wrote this. It also supports\nDjango 1.4, 1.5 and 1.6.\n\nThis project is very much in its infancy, and I'd be really interested\nto know how others get on. Also, if there's a better strategy or\nexisting library, I'd love to know about it.\n\nIf there's anything not made clear in this README, please open an\nissue.\n\nQuickstart\n----------\n\n```python\n\nfrom django_migration_testcase import MigrationTest\n\n\nclass MyMigrationTest(MigrationTest):\n\n    # See below for handling multiple apps\n    app_name = 'my_app'\n    # Or just the numbers, if you prefer brevity.\n    before = '0001_initial'\n    after = '0002_change_fields'\n\n    # Can have any name like test_*, is just a test method.\n    # MigrationTest subclasses django.test.TransactionTestCase\n    def test_migration(self):\n        # Load some data. Don't directly import models. At this point,\n        # the database is at self.before, and the models have fields\n        # set accordingly. Can get models from other apps with\n        # self.get_model_before('otherapp.OtherModel')\n\n        MyModel = self.get_model_before('MyModel')\n\n        # ... save some models\n\n        # Trigger the migration\n        self.run_migration()\n\n        # Now run some assertions based on what the data should now\n        # look like. The database will now be at self.after. To run\n        # queries via the models, reload the model.\n\n        MyModel = self.get_model_after('MyModel')\n```\n\n\nReverse Migrations\n------------------\n\nYou can test reverse migrations just like forward migrations. If you\nset `before = '0002'` and `after = '0001'` then when you call\n`self.run_migration()` in you test method it will run the reverse\nmigration from `0002`.\n\nAlternatively, you can write a test where you run the migrations\nforward the backwards again. For example:\n\n```python\n\nfrom django_migration_testcase import MigrationTest\n\n\nclass MyMigrationTest(MigrationTest):\n    app_name = 'my_app'\n    before = '0001'\n    after = '0002'\n\n    def test_migration(self):\n        # Set up some data...\n\n        # Run the migration forwards\n        self.run_migration()\n\n        # Check that the data looks right\n\n        # Run the migration back down again\n        self.run_reverse_migration()\n\n        # Check that the data has been put back as you expect.\n```\n\nMigrating Multiple Apps\n-----------------------\n\nIf you want to test that two apps in different apps play nicely\ntogether, you can set `self.before` and `self.after` as a list of\ntwo-tuples, each of which should be `([[app-name]],\n[[migration]])`. (This is done this way rather than as a dict because\norder may matter - migrations are run in the order they are listed.)\n\nEg\n```python\nclass MigrateBothMigrationTest(MigrationTest):\n    # Don't set app_name on the class, because there isn't one.\n    before = [('test_app', '0001'), ('test_second_app', '0001')]\n    after = [('test_app', '0002'), ('test_second_app', '0002')]\n```\n\nThen, in your `test_*` methods, when you need to get a model, you must\nspecify the app name (if you're only testing one app, then it can look\nat `self.app_name`). So you can't do\n`self.get_model_before('MyModel')`, you have to do\n`self.get_model_before('test_app.MyModel')`.\n\nMigration Versions\n------------------\n\nBy setting the migration version as `'zero'`, this sets the target\nmigration as before the first migration.\n\nIn cases where two migrations have the same number-prefix, you can\nspecify the full version to resolve this. Or you can use the full\nversion anyway in the name of explicitness.\n\nRelationships between models in different apps\n----------------------------------------------\n\nThis works in with Django's migrations. But (at present) doesn't with\nSouth. There's an additional problem with South, as the metadata for a\nmigration doesn't contain the state of all models, just those that\nwere frozen. So how does your test work out what a model should look\nlike for a migration, if the migration itself doesn't know? Answers in\na PR please, or just any suggestions.\n\nHow it works\n------------\n\nIn Django's migrations, when writing data migrations, rather than\ndirectly importing models, you load them using `apps.get_model()` --\nsee\n[here](https://docs.djangoproject.com/en/1.7/topics/migrations/#data-migrations).\nI've tried to unravel the migrations framework to do the same thing\nhere, so that we load models dynamically.\n\nThe `migrate` and `migrate_kwargs` methods\n------------------------------------------\n\nThe test method has a `migrate` method that takes an app name, a\nversion and an optional `fake` boolean. By default, this just calls:\n```python\ncall_command('migrate', app_name, version,\n             fake=fake, verbosity=0)\n```\n\nIf you need to alter your migrate command, you can either override this method, or you might just override `migrate_kwargs`, which by default sets `verbosity=0`. Extend this to pass more options/different options. Note that if you try to set a `fake` kwarg from this method, it will be ignored.\n\nTesting migration failures\n--------------------------\n\nSometimes you want a migration to fail, and then fix the problem by hand (Ex: Resolve issues with unique together).\n\nTo test this you will have to create a migration test provoking the problem. However if the data causing the migration error is not automatically cleaned up after the migration. The `tearDown`of `MigrationTest` will fail to migrate back the database in a good state and might create havoc in other tests. \n\nFor django 1.7+ (with database engines other than sqlite3) the helpful `@idempotent_transaction` decorator is available to automatically revert data created during the test (on both success and failure). \n\n```python\nfrom django_migration_testcase.base import idempotent_transaction\n\n@unittest.skipIf(django.VERSION \u003c (1, 7), 'Not supported by older django versions')\nclass TeardownFailCanBeAvoidedWithIdempotentTransaction(MigrationTest):\n    before = '0006'\n    after = '0007'\n\n    app_name = 'test_app'\n\n    @idempotent_transaction\n    def test_second_model_name_is_unique(self):\n        model_before = self.get_model_before('MySecondModel')\n        model_before.objects.create(name='foo')\n        model_before.objects.create(name='foo')\n        with self.assertRaises(IntegrityError):\n            self.run_migration()\n```\n\nFor django \u003c1.7 you will have to clean up the data by hand.\n\nFor more information see issue [Issue #33](https://github.com/plumdog/django_migration_testcase/issues/33) the [Pull Request #35](https://github.com/plumdog/django_migration_testcase/pull/35).\n\nTests\n-----\n\nThere's a test app that has four migrations. It is linked to different\nprojects within the test directory, one that uses postgres, and one\nthat uses sqlite3, and one for each but with South. To run the tests,\n`pip install django psycopg2 [south] -e .` then `./run_tests.sh`. Or,\nto cover all supported Django and Python versions: `pip install tox`\nthen `tox`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplumdog%2Fdjango_migration_testcase","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fplumdog%2Fdjango_migration_testcase","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplumdog%2Fdjango_migration_testcase/lists"}