root / env / lib / python2.7 / site-packages / south / management / commands / migrate.py @ d1a4905f
History | View | Annotate | Download (11.8 KB)
1 |
"""
|
---|---|
2 |
Migrate management command.
|
3 |
"""
|
4 |
|
5 |
import os.path, re, sys |
6 |
from optparse import make_option |
7 |
|
8 |
from django.core.management.base import BaseCommand |
9 |
from django.conf import settings |
10 |
from django.utils.importlib import import_module |
11 |
|
12 |
from south import migration |
13 |
from south.migration import Migrations |
14 |
from south.exceptions import NoMigrations |
15 |
from south.db import DEFAULT_DB_ALIAS |
16 |
|
17 |
class Command(BaseCommand): |
18 |
option_list = BaseCommand.option_list + ( |
19 |
make_option('--all', action='store_true', dest='all_apps', default=False, |
20 |
help='Run the specified migration for all apps.'),
|
21 |
make_option('--list', action='store_true', dest='show_list', default=False, |
22 |
help='List migrations noting those that have been applied'),
|
23 |
make_option('--changes', action='store_true', dest='show_changes', default=False, |
24 |
help='List changes for migrations'),
|
25 |
make_option('--skip', action='store_true', dest='skip', default=False, |
26 |
help='Will skip over out-of-order missing migrations'),
|
27 |
make_option('--merge', action='store_true', dest='merge', default=False, |
28 |
help='Will run out-of-order missing migrations as they are - no rollbacks.'),
|
29 |
make_option('--no-initial-data', action='store_true', dest='no_initial_data', default=False, |
30 |
help='Skips loading initial data if specified.'),
|
31 |
make_option('--fake', action='store_true', dest='fake', default=False, |
32 |
help="Pretends to do the migrations, but doesn't actually execute them."),
|
33 |
make_option('--db-dry-run', action='store_true', dest='db_dry_run', default=False, |
34 |
help="Doesn't execute the SQL generated by the db methods, and doesn't store a record that the migration(s) occurred. Useful to test migrations before applying them."),
|
35 |
make_option('--delete-ghost-migrations', action='store_true', dest='delete_ghosts', default=False, |
36 |
help="Tells South to delete any 'ghost' migrations (ones in the database but not on disk)."),
|
37 |
make_option('--ignore-ghost-migrations', action='store_true', dest='ignore_ghosts', default=False, |
38 |
help="Tells South to ignore any 'ghost' migrations (ones in the database but not on disk) and continue to apply new migrations."),
|
39 |
make_option('--noinput', action='store_false', dest='interactive', default=True, |
40 |
help='Tells Django to NOT prompt the user for input of any kind.'),
|
41 |
make_option('--database', action='store', dest='database', |
42 |
default=DEFAULT_DB_ALIAS, help='Nominates a database to synchronize. '
|
43 |
'Defaults to the "default" database.'),
|
44 |
) |
45 |
if '--verbosity' not in [opt.get_opt_string() for opt in BaseCommand.option_list]: |
46 |
option_list += ( |
47 |
make_option('--verbosity', action='store', dest='verbosity', default='1', |
48 |
type='choice', choices=['0', '1', '2'], |
49 |
help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'),
|
50 |
) |
51 |
help = "Runs migrations for all apps."
|
52 |
args = "[appname] [migrationname|zero] [--all] [--list] [--skip] [--merge] [--no-initial-data] [--fake] [--db-dry-run] [--database=dbalias]"
|
53 |
|
54 |
def handle(self, app=None, target=None, skip=False, merge=False, backwards=False, fake=False, db_dry_run=False, show_list=False, show_changes=False, database=DEFAULT_DB_ALIAS, delete_ghosts=False, ignore_ghosts=False, **options): |
55 |
|
56 |
# NOTE: THIS IS DUPLICATED FROM django.core.management.commands.syncdb
|
57 |
# This code imports any module named 'management' in INSTALLED_APPS.
|
58 |
# The 'management' module is the preferred way of listening to post_syncdb
|
59 |
# signals, and since we're sending those out with create_table migrations,
|
60 |
# we need apps to behave correctly.
|
61 |
for app_name in settings.INSTALLED_APPS: |
62 |
try:
|
63 |
import_module('.management', app_name)
|
64 |
except ImportError, exc: |
65 |
msg = exc.args[0]
|
66 |
if not msg.startswith('No module named') or 'management' not in msg: |
67 |
raise
|
68 |
# END DJANGO DUPE CODE
|
69 |
|
70 |
# if all_apps flag is set, shift app over to target
|
71 |
if options.get('all_apps', False): |
72 |
target = app |
73 |
app = None
|
74 |
|
75 |
# Migrate each app
|
76 |
if app:
|
77 |
try:
|
78 |
apps = [Migrations(app)] |
79 |
except NoMigrations:
|
80 |
print "The app '%s' does not appear to use migrations." % app |
81 |
print "./manage.py migrate " + self.args |
82 |
return
|
83 |
else:
|
84 |
apps = list(migration.all_migrations())
|
85 |
|
86 |
# Do we need to show the list of migrations?
|
87 |
if show_list and apps: |
88 |
list_migrations(apps, database, **options) |
89 |
|
90 |
if show_changes and apps: |
91 |
show_migration_changes(apps) |
92 |
|
93 |
if not (show_list or show_changes): |
94 |
|
95 |
for app in apps: |
96 |
result = migration.migrate_app( |
97 |
app, |
98 |
target_name = target, |
99 |
fake = fake, |
100 |
db_dry_run = db_dry_run, |
101 |
verbosity = int(options.get('verbosity', 0)), |
102 |
interactive = options.get('interactive', True), |
103 |
load_initial_data = not options.get('no_initial_data', False), |
104 |
merge = merge, |
105 |
skip = skip, |
106 |
database = database, |
107 |
delete_ghosts = delete_ghosts, |
108 |
ignore_ghosts = ignore_ghosts, |
109 |
) |
110 |
if result is False: |
111 |
sys.exit(1) # Migration failed, so the command fails. |
112 |
|
113 |
|
114 |
def list_migrations(apps, database = DEFAULT_DB_ALIAS, **options): |
115 |
"""
|
116 |
Prints a list of all available migrations, and which ones are currently applied.
|
117 |
Accepts a list of Migrations instances.
|
118 |
"""
|
119 |
from south.models import MigrationHistory |
120 |
applied_migrations = MigrationHistory.objects.filter(app_name__in=[app.app_label() for app in apps]) |
121 |
if database != DEFAULT_DB_ALIAS:
|
122 |
applied_migrations = applied_migrations.using(database) |
123 |
applied_migration_names = ['%s.%s' % (mi.app_name,mi.migration) for mi in applied_migrations] |
124 |
|
125 |
print
|
126 |
for app in apps: |
127 |
print " " + app.app_label() |
128 |
# Get the migrations object
|
129 |
for migration in app: |
130 |
if migration.app_label() + "." + migration.name() in applied_migration_names: |
131 |
applied_migration = applied_migrations.get(app_name=migration.app_label(), migration=migration.name()) |
132 |
print format_migration_list_item(migration.name(), applied=applied_migration.applied, **options)
|
133 |
else:
|
134 |
print format_migration_list_item(migration.name(), applied=False, **options) |
135 |
print
|
136 |
|
137 |
def show_migration_changes(apps): |
138 |
"""
|
139 |
Prints a list of all available migrations, and which ones are currently applied.
|
140 |
Accepts a list of Migrations instances.
|
141 |
|
142 |
Much simpler, less clear, and much less robust version:
|
143 |
grep "ing " migrations/*.py
|
144 |
"""
|
145 |
for app in apps: |
146 |
print app.app_label()
|
147 |
# Get the migrations objects
|
148 |
migrations = [migration for migration in app] |
149 |
# we use reduce to compare models in pairs, not to generate a value
|
150 |
reduce(diff_migrations, migrations)
|
151 |
|
152 |
def format_migration_list_item(name, applied=True, **options): |
153 |
if applied:
|
154 |
if int(options.get('verbosity')) >= 2: |
155 |
return ' (*) %-80s (applied %s)' % (name, applied) |
156 |
else:
|
157 |
return ' (*) %s' % name |
158 |
else:
|
159 |
return ' ( ) %s' % name |
160 |
|
161 |
def diff_migrations(migration1, migration2): |
162 |
|
163 |
def model_name(models, model): |
164 |
return models[model].get('Meta', {}).get('object_name', model) |
165 |
|
166 |
def field_name(models, model, field): |
167 |
return '%s.%s' % (model_name(models, model), field) |
168 |
|
169 |
print " " + migration2.name() |
170 |
|
171 |
models1 = migration1.migration_class().models |
172 |
models2 = migration2.migration_class().models |
173 |
|
174 |
# find new models
|
175 |
for model in models2.keys(): |
176 |
if not model in models1.keys(): |
177 |
print ' added model %s' % model_name(models2, model) |
178 |
|
179 |
# find removed models
|
180 |
for model in models1.keys(): |
181 |
if not model in models2.keys(): |
182 |
print ' removed model %s' % model_name(models1, model) |
183 |
|
184 |
# compare models
|
185 |
for model in models1: |
186 |
if model in models2: |
187 |
|
188 |
# find added fields
|
189 |
for field in models2[model]: |
190 |
if not field in models1[model]: |
191 |
print ' added field %s' % field_name(models2, model, field) |
192 |
|
193 |
# find removed fields
|
194 |
for field in models1[model]: |
195 |
if not field in models2[model]: |
196 |
print ' removed field %s' % field_name(models1, model, field) |
197 |
|
198 |
# compare fields
|
199 |
for field in models1[model]: |
200 |
if field in models2[model]: |
201 |
|
202 |
name = field_name(models1, model, field) |
203 |
|
204 |
# compare field attributes
|
205 |
field_value1 = models1[model][field] |
206 |
field_value2 = models2[model][field] |
207 |
|
208 |
# if a field has become a class, or vice versa
|
209 |
if type(field_value1) != type(field_value2): |
210 |
print ' type of %s changed from %s to %s' % ( |
211 |
name, field_value1, field_value2) |
212 |
|
213 |
# if class
|
214 |
elif isinstance(field_value1, dict): |
215 |
# print ' %s is a class' % name
|
216 |
pass
|
217 |
|
218 |
# else regular field
|
219 |
else:
|
220 |
|
221 |
type1, attr_list1, field_attrs1 = models1[model][field] |
222 |
type2, attr_list2, field_attrs2 = models2[model][field] |
223 |
|
224 |
if type1 != type2:
|
225 |
print ' %s type changed from %s to %s' % ( |
226 |
name, type1, type2) |
227 |
|
228 |
if attr_list1 != []:
|
229 |
print ' %s list %s is not []' % ( |
230 |
name, attr_list1) |
231 |
if attr_list2 != []:
|
232 |
print ' %s list %s is not []' % ( |
233 |
name, attr_list2) |
234 |
if attr_list1 != attr_list2:
|
235 |
print ' %s list changed from %s to %s' % ( |
236 |
name, attr_list1, attr_list2) |
237 |
|
238 |
# find added field attributes
|
239 |
for attr in field_attrs2: |
240 |
if not attr in field_attrs1: |
241 |
print ' added %s attribute %s=%s' % ( |
242 |
name, attr, field_attrs2[attr]) |
243 |
|
244 |
# find removed field attributes
|
245 |
for attr in field_attrs1: |
246 |
if not attr in field_attrs2: |
247 |
print ' removed attribute %s(%s=%s)' % ( |
248 |
name, attr, field_attrs1[attr]) |
249 |
|
250 |
# compare field attributes
|
251 |
for attr in field_attrs1: |
252 |
if attr in field_attrs2: |
253 |
|
254 |
value1 = field_attrs1[attr] |
255 |
value2 = field_attrs2[attr] |
256 |
if value1 != value2:
|
257 |
print ' %s attribute %s changed from %s to %s' % ( |
258 |
name, attr, value1, value2) |
259 |
|
260 |
return migration2
|