Project

General

Profile

0001-safely-migrate-a-projet-5781.patch

Thomas Noël, 22 October 2014 12:31 PM

Download (5.17 KB)

View differences:

Subject: [PATCH] safely migrate a projet (#5781)

safemigration workflow is :
* syncdb
* for each app installed, with migrations available but never migrated:
        migrate --fake <app> 0001
* migrate
 entrouvert/djommon/safemigrate/__init__.py         |  0
 .../djommon/safemigrate/management/__init__.py     |  0
 .../safemigrate/management/commands/__init__.py    |  0
 .../safemigrate/management/commands/safemigrate.py | 76 ++++++++++++++++++++++
 entrouvert/djommon/safemigrate/models.py           |  0
 5 files changed, 76 insertions(+)
 create mode 100644 entrouvert/djommon/safemigrate/__init__.py
 create mode 100644 entrouvert/djommon/safemigrate/management/__init__.py
 create mode 100644 entrouvert/djommon/safemigrate/management/commands/__init__.py
 create mode 100644 entrouvert/djommon/safemigrate/management/commands/safemigrate.py
 create mode 100644 entrouvert/djommon/safemigrate/models.py
entrouvert/djommon/safemigrate/management/commands/safemigrate.py
1
from django.core.management.base import NoArgsCommand
2
from django.db import connections, router, models
3

  
4
from south.models import MigrationHistory
5
from south.management.commands import syncdb, migrate
6
from django.core.management.commands import syncdb as django_syncdb
7

  
8

  
9
class Command(NoArgsCommand):
10
    option_list = django_syncdb.Command.option_list
11
    help = '''syncdb + migrate, with "migrate --fake app 0001" for apps with new initial migrations'''
12

  
13
    def handle_noargs(self, *args, **options):
14

  
15
        # step 1 : syncdb, create all apps without migrations
16
        syncdb.Command().execute(migrate_all=False, migrate=False, **options)
17

  
18
        # step 2 : detect and "fake 0001" all installed apps that had never
19
        # migrated (applications in database but not in south history)
20

  
21
        # detect installed models
22
        # (code borrowed from django syncdb command)
23
        db = options.get('database')
24
        connection = connections[db]
25
        cursor = connection.cursor()
26
        tables = connection.introspection.table_names()
27
        def model_in_database(model):
28
            opts = model._meta
29
            converter = connection.introspection.table_name_converter
30
            return (converter(opts.db_table) in tables) or \
31
                (opts.auto_created and converter(opts.auto_created._meta.db_table) in tables)
32

  
33
        # list all applications with south migration
34
        # (code borrowed from south migrate command)
35
        from south import migration
36
        apps = list(migration.all_migrations())
37
        applied_migrations = MigrationHistory.objects.filter(app_name__in=[app.app_label() for app in apps])
38
        applied_migrations_lookup = dict(('%s.%s' % (mi.app_name, mi.migration), mi) for mi in applied_migrations)
39

  
40
        print
41
        print 'Status after syncdb:'
42

  
43
        for app in apps:
44
            # for each app with migrations, list already applied migrations
45
            applied_migrations = []
46
            for migration in app:
47
                full_name = migration.app_label() + "." + migration.name()
48
                if full_name in applied_migrations_lookup:
49
                    applied_migration = applied_migrations_lookup[full_name]
50
                    if applied_migration.applied:
51
                        applied_migrations.append(migration.name())
52

  
53
            # try all models in database, if there none, the application is new
54
            # (because south syncdb does not create any tables)
55
            new = True
56
            for m in models.get_models(app.get_application().models):
57
                if model_in_database(m):
58
                    new = False
59
                    break
60

  
61
            if new:
62
                status = 'application-is-new'
63
            elif not applied_migrations:
64
                status = 'migration-is-new'
65
            else:
66
                status = 'normal'
67

  
68
            print ' - %s: %s' % (app.app_label(), status)
69

  
70
            if status == 'migration-is-new':
71
                print '   need fake migration to 0001_initial'
72
                # migrate --fake gdc 0001
73
                migrate.Command().execute(app=app.app_label(), target='0001', fake=True)
74

  
75
        print
76
        migrate.Command().execute(**options)