import unittest import django from django.db import IntegrityError from django.conf import settings from django_migration_testcase import MigrationTest from django_migration_testcase.base import InvalidModelStateError, idempotent_transaction class ExampleMigrationTest(MigrationTest): before = '0001_initial' after = '0002_mymodel_number' app_name = 'test_app' def test_migration(self): MyModel = self.get_model_before('MyModel') for i in range(10): mymodel = MyModel() mymodel.name = 'example name {}'.format(i) mymodel.save() self.assertEqual(MyModel.objects.count(), 10) self.run_migration() MyModel = self.get_model_after('MyModel') self.assertEqual(MyModel.objects.count(), 10) def test_run_reverse_migration(self): MyModel = self.get_model_before('MyModel') for i in range(10): mymodel = MyModel() mymodel.name = 'example name {}'.format(i) mymodel.save() self.assertEqual(MyModel.objects.count(), 10) self.run_migration() MyModel = self.get_model_after('MyModel') self.assertEqual(MyModel.objects.count(), 10) self.run_reverse_migration() self.assertEqual(MyModel.objects.count(), 10) def test_invalid_field(self): MyModel = self.get_model_before('MyModel') mymodel = MyModel() mymodel.number = 10 mymodel.save() mymodel = MyModel.objects.get() with self.assertRaises(AttributeError): mymodel.number self.run_migration() MyModel = self.get_model_after('MyModel') mymodel = MyModel.objects.get() self.assertEqual(mymodel.number, None) mymodel.number = 10 mymodel.save() mymodel = MyModel.objects.get() self.assertEqual(mymodel.number, 10) def field_names(model_class): try: return model_class._meta.get_all_field_names() except AttributeError: return [f.name for f in model_class._meta.get_fields()] class AddDoubleNumberTest(MigrationTest): before = '0002_mymodel_number' after = '0003_mymodel_double_number' app_name = 'test_app' def test_migration(self): MyModel = self.get_model_before('MyModel') self.assertNotIn('double_number', field_names(MyModel)) self.run_migration() MyModel = self.get_model_after('MyModel') self.assertIn('double_number', field_names(MyModel)) class MigrationsByNumberOnlyTest(MigrationTest): before = '0002' after = '0003' app_name = 'test_app' def test_migration(self): MyModel = self.get_model_before('MyModel') self.assertNotIn('double_number', field_names(MyModel)) self.run_migration() MyModel = self.get_model_after('MyModel') self.assertIn('double_number', field_names(MyModel)) class PopulateDoubleNumberTest(MigrationTest): before = '0003_mymodel_double_number' after = '0004_populate_mymodel_double_number' app_name = 'test_app' def test_migration(self): MyModel = self.get_model_before('MyModel') for i in range(10): mymodel = MyModel() mymodel.name = 'example name {}'.format(i) mymodel.number = i mymodel.save() self.run_migration() MyModel = self.get_model_after('MyModel') for mymodel in MyModel.objects.all(): self.assertEqual(mymodel.number * 2, mymodel.double_number) class GetModelMigrationTest(MigrationTest): before = '0001_initial' after = '0002_mymodel_number' app_name = 'test_app' def test_migration(self): MyModel = self.get_model_before('test_app.MyModel') self.assertEqual(MyModel.__name__, 'MyModel') self.run_migration() MyModel = self.get_model_after('test_app.MyModel') self.assertEqual(MyModel.__name__, 'MyModel') class ForeignKeyTest(MigrationTest): before = '0004_populate_mymodel_double_number' after = '0005_foreignmodel' app_name = 'test_app' def test_migration(self): MyModel = self.get_model_before('test_app.MyModel') self.assertEqual(MyModel.__name__, 'MyModel') self.run_migration() ForeignModel = self.get_model_after('test_app.ForeignModel') self.assertEqual(ForeignModel.__name__, 'ForeignModel') MyModel = self.get_model_after('test_app.MyModel') self.assertEqual(MyModel.__name__, 'MyModel') my = MyModel(name='test_my', number=1, double_number=3.14) my.save() ForeignModel(name='test_foreign', my=my) def test_migration2(self): """Same test as test_migration, but this one passes.""" MyModel = self.get_model_before('test_app.MyModel') self.assertEqual(MyModel.__name__, 'MyModel') self.run_migration() ForeignModel = self.get_model_after('test_app.ForeignModel') self.assertEqual(ForeignModel.__name__, 'ForeignModel') # get_model_before/get_model_after seems to not get the same model as # this crazy thing. if django.VERSION >= (2, 0): MyModel = ForeignModel.my.field.related_model else: MyModel = ForeignModel.my.field.rel.to self.assertEqual(MyModel.__name__, 'MyModel') my = MyModel(name='test_my', number=1, double_number=3.14) my.save() ForeignModel(name='test_foreign', my=my) def test_migration_clearly(self): """A clear illustration of the problem.""" self.run_migration() ForeignModel = self.get_model_after('test_app.ForeignModel') # get_model_before/get_model_after seems to not get the same model as # this crazy thing. if django.VERSION >= (2, 0): MyModel = ForeignModel.my.field.related_model else: MyModel = ForeignModel.my.field.rel.to MyModel2 = self.get_model_after('test_app.MyModel') self.assertEqual(MyModel, MyModel2) class UtilsMigrationTest(MigrationTest): before = '0001_initial' after = '0002_mymodel_number' app_name = 'test_app' def test_migration_not_run_exception(self): with self.assertRaises(InvalidModelStateError): self.get_model_after('MyModel') with self.assertRaises(InvalidModelStateError): self.run_reverse_migration() def test_migration_already_run_exception(self): self.run_migration() with self.assertRaises(InvalidModelStateError): self.get_model_before('MyModel') class MigrateFromZero(MigrationTest): before = 'zero' after = '0001_initial' app_name = 'test_app' def test_model_exists(self): with self.assertRaises(LookupError): self.get_model_before('MyModel') self.run_migration() MyModel = self.get_model_after('MyModel') self.assertEqual(MyModel.__name__, 'MyModel') class TeardownCanFail(MigrationTest): before = '0006' after = '0007' app_name = 'test_app' def test_second_model_name_is_unique(self): model_before = self.get_model_before('MySecondModel') model_before.objects.create(name='foo') model_before.objects.create(name='foo') with self.assertRaises(IntegrityError): self.run_migration() def tearDown(self): self.assertTrue(self.get_model_before('MySecondModel').objects.all().exists()) with self.assertRaises(IntegrityError): # tearDown fails since migrations runs again with the data super(TeardownCanFail, self).tearDown() self.get_model_before('MySecondModel').objects.all().delete() super(TeardownCanFail, self).tearDown() @unittest.skipIf(django.VERSION < (1, 7), 'Not supported by older django versions') @unittest.skipIf(django.VERSION >= (2, 0) and settings.DATABASES['default']['ENGINE'] == 'django.db.backends.sqlite3', 'Not supported with django2 with sqlite3') class TeardownFailCanBeAvoidedWithIdempotentTransaction(MigrationTest): before = '0006' after = '0007' app_name = 'test_app' @idempotent_transaction def test_second_model_name_is_unique(self): model_before = self.get_model_before('MySecondModel') model_before.objects.create(name='foo') model_before.objects.create(name='foo') with self.assertRaises(IntegrityError): self.run_migration()