From b9b6038e7f7dac95d841adf91d69372c617cd700 Mon Sep 17 00:00:00 2001 From: Manuel Jeckelmann Date: Sun, 18 Oct 2015 01:39:48 +0200 Subject: [PATCH] Added a reproducing case for the VM2M migration issue described in #56 --- .travis.yml | 1 + cleanerversion/settings/base.py | 2 +- migrations_tests/__init__.py | 0 migrations_tests/migrations/0001_initial.py | 53 ++++++++++++++ migrations_tests/migrations/__init__.py | 0 migrations_tests/models.py | 12 ++++ migrations_tests/tests/__init__.py | 0 migrations_tests/tests/test_migrations.py | 77 +++++++++++++++++++++ 8 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 migrations_tests/__init__.py create mode 100644 migrations_tests/migrations/0001_initial.py create mode 100644 migrations_tests/migrations/__init__.py create mode 100644 migrations_tests/models.py create mode 100644 migrations_tests/tests/__init__.py create mode 100644 migrations_tests/tests/test_migrations.py diff --git a/.travis.yml b/.travis.yml index 85a822a..17d44e9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,7 @@ addons: install: - pip install tox - pip install coveralls + - pip install django-migration-testcase # Ensure PostgreSQL-DB to be configured correctly before_script: diff --git a/cleanerversion/settings/base.py b/cleanerversion/settings/base.py index 546abce..df22513 100644 --- a/cleanerversion/settings/base.py +++ b/cleanerversion/settings/base.py @@ -65,6 +65,7 @@ 'django.contrib.staticfiles', 'versions', 'versions_tests', + 'migrations_tests', ) MIDDLEWARE_CLASSES = ( @@ -90,4 +91,3 @@ STATIC_URL = '/static/' - diff --git a/migrations_tests/__init__.py b/migrations_tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/migrations_tests/migrations/0001_initial.py b/migrations_tests/migrations/0001_initial.py new file mode 100644 index 0000000..05dbdfa --- /dev/null +++ b/migrations_tests/migrations/0001_initial.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import versions.models + + +class Migration(migrations.Migration): + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='MyMigratingModelA', + fields=[ + ('id', models.CharField(max_length=36, serialize=False, primary_key=True)), + ('identity', models.CharField(max_length=36)), + ('version_start_date', models.DateTimeField()), + ('version_end_date', models.DateTimeField(default=None, null=True, blank=True)), + ('version_birth_date', models.DateTimeField()), + ('name', models.CharField(max_length=10)), + ], + options={ + 'abstract': False, + }, + bases=(models.Model,), + ), + migrations.CreateModel( + name='MyMigratingModelB', + fields=[ + ('id', models.CharField(max_length=36, serialize=False, primary_key=True)), + ('identity', models.CharField(max_length=36)), + ('version_start_date', models.DateTimeField()), + ('version_end_date', models.DateTimeField(default=None, null=True, blank=True)), + ('version_birth_date', models.DateTimeField()), + ('identifier', models.CharField(max_length=10)), + ('a_models', versions.models.VersionedManyToManyField(to='migrations_tests.MyMigratingModelA')), + ], + options={ + 'abstract': False, + }, + bases=(models.Model,), + ), + migrations.AlterUniqueTogether( + name='mymigratingmodelb', + unique_together=set([('id', 'identity')]), + ), + migrations.AlterUniqueTogether( + name='mymigratingmodela', + unique_together=set([('id', 'identity')]), + ), + ] diff --git a/migrations_tests/migrations/__init__.py b/migrations_tests/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/migrations_tests/models.py b/migrations_tests/models.py new file mode 100644 index 0000000..4baee55 --- /dev/null +++ b/migrations_tests/models.py @@ -0,0 +1,12 @@ +from django.db.models.fields import CharField +from versions.models import Versionable, VersionedManyToManyField + + +############################################ +# MigrationTest models +class MyMigratingModelA(Versionable): + name = CharField(max_length=10) + +class MyMigratingModelB(Versionable): + identifier = CharField(max_length=10) + a_models = VersionedManyToManyField('MyMigratingModelA', related_name='b_models') diff --git a/migrations_tests/tests/__init__.py b/migrations_tests/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/migrations_tests/tests/test_migrations.py b/migrations_tests/tests/test_migrations.py new file mode 100644 index 0000000..62b6469 --- /dev/null +++ b/migrations_tests/tests/test_migrations.py @@ -0,0 +1,77 @@ +import os +import re +from django.apps import apps +from django.core.management import call_command +from django.db.migrations.loader import MigrationLoader +from django_migration_testcase import MigrationTest +from django.db import migrations +import sys +import versions + + +class Migration(migrations.Migration): + + dependencies = [ + ('migrations_tests', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='mymigratingmodelb', + name='a_models', + field=versions.models.VersionedManyToManyField(related_name='b_models', to='migrations_tests.MyMigratingModelA'), + preserve_default=True, + ), + ] + +class M2MRelatedNameMigration(MigrationTest): + app = 'migrations_tests' + before = [(app, '0001_initial')] + after = [] + after_migration_file = None + + def call_makemigrations(self, app_name=None): + if not app_name: + app_name = self.app + try: + mig_filename_holder = "/tmp/migrations_filename.txt" + with open(mig_filename_holder, 'w') as f: + call_command('makemigrations', app_name, + verbosity=1, no_initial_data=True, stdout=f) + fh = open(mig_filename_holder, 'r') + out = fh.read().split('\n')[1] + fh.close() + match = re.match(r'.*((\d{4}_auto\w+)\.py)', out) + out_mig = match.group(2) + self.after_migration_file = "./%s/migrations/%s" % (app_name, match.group(1)) + finally: + fh.close() + os.remove(mig_filename_holder) + with open(self.after_migration_file, 'r') as fh: + self.after_migration_content = fh.read() + self.after = [(app_name, out_mig)] + + def tearDown(self): + try: + os.remove(self.after_migration_file) + except Exception as e: + print str(e) + super(M2MRelatedNameMigration, self).tearDown() + + def test_migration(self): + MyMigratingModelA = self.get_model_before('migrations_tests.MyMigratingModelA') + MyMigratingModelB = self.get_model_before('migrations_tests.MyMigratingModelB') + + self.assertIn('mymigratingmodelb_set', MyMigratingModelA.__dict__) + + # Make migration file + self.call_makemigrations() + print "----------\n%s\n----------\n" % self.after_migration_content + + # self.run_migration runs the newly created migration and thus provokes an error: + # "AttributeError: 'VersionedManyToManyField' object has no attribute '_m2m_reverse_name_cache'" + self.assertRaises(AttributeError, self.run_migration) + + # MyMigratingModelA = self.get_model_after('migrations_tests.MyMigratingModelA') + # MyMigratingModelB = self.get_model_after('migrations_tests.MyMigratingModelB') + # self.assertIn('b_models', MyMigratingModelA.__dict__)