mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Initial stab at a migrate command, it's probably quite a way off.
This commit is contained in:
		
							
								
								
									
										210
									
								
								django/core/management/commands/migrate.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								django/core/management/commands/migrate.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,210 @@ | |||||||
|  | from optparse import make_option | ||||||
|  | import itertools | ||||||
|  | import traceback | ||||||
|  |  | ||||||
|  | from django.conf import settings | ||||||
|  | from django.core.management import call_command | ||||||
|  | from django.core.management.base import NoArgsCommand | ||||||
|  | from django.core.management.color import color_style | ||||||
|  | from django.core.management.sql import custom_sql_for_model, emit_post_sync_signal, emit_pre_sync_signal | ||||||
|  | from django.db import connections, router, transaction, models, DEFAULT_DB_ALIAS | ||||||
|  | from django.db.migrations.executor import MigrationExecutor | ||||||
|  | from django.utils.datastructures import SortedDict | ||||||
|  | from django.utils.importlib import import_module | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Command(NoArgsCommand): | ||||||
|  |     option_list = NoArgsCommand.option_list + ( | ||||||
|  |         make_option('--noinput', action='store_false', dest='interactive', default=True, | ||||||
|  |             help='Tells Django to NOT prompt the user for input of any kind.'), | ||||||
|  |         make_option('--no-initial-data', action='store_false', dest='load_initial_data', default=True, | ||||||
|  |             help='Tells Django not to load any initial data after database synchronization.'), | ||||||
|  |         make_option('--database', action='store', dest='database', | ||||||
|  |             default=DEFAULT_DB_ALIAS, help='Nominates a database to synchronize. ' | ||||||
|  |                 'Defaults to the "default" database.'), | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     help = "Updates database schema. Manages both apps with migrations and those without." | ||||||
|  |  | ||||||
|  |     def handle_noargs(self, **options): | ||||||
|  |  | ||||||
|  |         self.verbosity = int(options.get('verbosity')) | ||||||
|  |         self.interactive = options.get('interactive') | ||||||
|  |         self.show_traceback = options.get('traceback') | ||||||
|  |         self.load_initial_data = options.get('load_initial_data') | ||||||
|  |  | ||||||
|  |         self.style = color_style() | ||||||
|  |  | ||||||
|  |         # Import the 'management' module within each installed app, to register | ||||||
|  |         # dispatcher events. | ||||||
|  |         for app_name in settings.INSTALLED_APPS: | ||||||
|  |             try: | ||||||
|  |                 import_module('.management', app_name) | ||||||
|  |             except ImportError as exc: | ||||||
|  |                 # This is slightly hackish. We want to ignore ImportErrors | ||||||
|  |                 # if the "management" module itself is missing -- but we don't | ||||||
|  |                 # want to ignore the exception if the management module exists | ||||||
|  |                 # but raises an ImportError for some reason. The only way we | ||||||
|  |                 # can do this is to check the text of the exception. Note that | ||||||
|  |                 # we're a bit broad in how we check the text, because different | ||||||
|  |                 # Python implementations may not use the same text. | ||||||
|  |                 # CPython uses the text "No module named management" | ||||||
|  |                 # PyPy uses "No module named myproject.myapp.management" | ||||||
|  |                 msg = exc.args[0] | ||||||
|  |                 if not msg.startswith('No module named') or 'management' not in msg: | ||||||
|  |                     raise | ||||||
|  |  | ||||||
|  |         # Get the database we're operating from | ||||||
|  |         db = options.get('database') | ||||||
|  |         connection = connections[db] | ||||||
|  |  | ||||||
|  |         # Work out which apps have migrations and which do not | ||||||
|  |         if self.verbosity >= 1: | ||||||
|  |             self.stdout.write(self.style.MIGRATE_HEADING("Calculating migration plan:")) | ||||||
|  |         executor = MigrationExecutor(connection, self.migration_progress_callback) | ||||||
|  |         if self.verbosity >= 1: | ||||||
|  |             self.stdout.write(self.style.MIGRATE_LABEL("  Apps without migrations: ") + (", ".join(executor.loader.unmigrated_apps) or "(none)")) | ||||||
|  |  | ||||||
|  |         # Work out what targets they want, and then make a migration plan | ||||||
|  |         # TODO: Let users select targets | ||||||
|  |         targets = executor.loader.graph.leaf_nodes() | ||||||
|  |         plan = executor.migration_plan(targets) | ||||||
|  |  | ||||||
|  |         if self.verbosity >= 1: | ||||||
|  |             self.stdout.write(self.style.MIGRATE_LABEL("  Apps with migrations:    ") + (", ".join(executor.loader.disk_migrations) or "(none)")) | ||||||
|  |  | ||||||
|  |         # Run the syncdb phase. | ||||||
|  |         # If you ever manage to get rid of this, I owe you many, many drinks. | ||||||
|  |         self.stdout.write(self.style.MIGRATE_HEADING("Synchronizing apps without migrations:")) | ||||||
|  |         self.sync_apps(connection, executor.loader.unmigrated_apps) | ||||||
|  |  | ||||||
|  |         # Migrate! | ||||||
|  |         if self.verbosity >= 1: | ||||||
|  |             self.stdout.write(self.style.MIGRATE_HEADING("Running migrations:")) | ||||||
|  |         if not plan: | ||||||
|  |             if self.verbosity >= 1: | ||||||
|  |                 self.stdout.write("  No migrations needed.") | ||||||
|  |         else: | ||||||
|  |             executor.migrate(targets, plan) | ||||||
|  |  | ||||||
|  |     def migration_progress_callback(self, action, migration): | ||||||
|  |         if self.verbosity >= 1: | ||||||
|  |             if action == "apply_start": | ||||||
|  |                 self.stdout.write("  Applying %s... " % migration) | ||||||
|  |                 self.stdout.flush() | ||||||
|  |             elif action == "apply_success": | ||||||
|  |                 self.stdout.write("  OK\n") | ||||||
|  |             elif action == "unapply_start": | ||||||
|  |                 self.stdout.write("  Unapplying %s... " % migration) | ||||||
|  |                 self.stdout.flush() | ||||||
|  |             elif action == "unapply_success": | ||||||
|  |                 self.stdout.write("  OK\n") | ||||||
|  |  | ||||||
|  |     def sync_apps(self, connection, apps): | ||||||
|  |         "Runs the old syncdb-style operation on a list of apps." | ||||||
|  |         cursor = connection.cursor() | ||||||
|  |  | ||||||
|  |         # Get a list of already installed *models* so that references work right. | ||||||
|  |         tables = connection.introspection.table_names() | ||||||
|  |         seen_models = connection.introspection.installed_models(tables) | ||||||
|  |         created_models = set() | ||||||
|  |         pending_references = {} | ||||||
|  |  | ||||||
|  |         # Build the manifest of apps and models that are to be synchronized | ||||||
|  |         all_models = [ | ||||||
|  |             (app.__name__.split('.')[-2], | ||||||
|  |                 [ | ||||||
|  |                     m for m in models.get_models(app, include_auto_created=True) | ||||||
|  |                     if router.allow_syncdb(connection.alias, m) | ||||||
|  |                 ]) | ||||||
|  |             for app in models.get_apps() if app.__name__.split('.')[-2] in apps | ||||||
|  |         ] | ||||||
|  |  | ||||||
|  |         def model_installed(model): | ||||||
|  |             opts = model._meta | ||||||
|  |             converter = connection.introspection.table_name_converter | ||||||
|  |             # Note that if a model is unmanaged we short-circuit and never try to install it | ||||||
|  |             return not ((converter(opts.db_table) in tables) or | ||||||
|  |                 (opts.auto_created and converter(opts.auto_created._meta.db_table) in tables)) | ||||||
|  |  | ||||||
|  |         manifest = SortedDict( | ||||||
|  |             (app_name, list(filter(model_installed, model_list))) | ||||||
|  |             for app_name, model_list in all_models | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         create_models = set([x for x in itertools.chain(*manifest.values())]) | ||||||
|  |         emit_pre_sync_signal(create_models, self.verbosity, self.interactive, connection.alias) | ||||||
|  |  | ||||||
|  |         # Create the tables for each model | ||||||
|  |         if self.verbosity >= 1: | ||||||
|  |             self.stdout.write("  Creating tables...\n") | ||||||
|  |         with transaction.commit_on_success_unless_managed(using=connection.alias): | ||||||
|  |             for app_name, model_list in manifest.items(): | ||||||
|  |                 for model in model_list: | ||||||
|  |                     # Create the model's database table, if it doesn't already exist. | ||||||
|  |                     if self.verbosity >= 3: | ||||||
|  |                         self.stdout.write("    Processing %s.%s model\n" % (app_name, model._meta.object_name)) | ||||||
|  |                     sql, references = connection.creation.sql_create_model(model, self.style, seen_models) | ||||||
|  |                     seen_models.add(model) | ||||||
|  |                     created_models.add(model) | ||||||
|  |                     for refto, refs in references.items(): | ||||||
|  |                         pending_references.setdefault(refto, []).extend(refs) | ||||||
|  |                         if refto in seen_models: | ||||||
|  |                             sql.extend(connection.creation.sql_for_pending_references(refto, self.style, pending_references)) | ||||||
|  |                     sql.extend(connection.creation.sql_for_pending_references(model, self.style, pending_references)) | ||||||
|  |                     if self.verbosity >= 1 and sql: | ||||||
|  |                         self.stdout.write("    Creating table %s\n" % model._meta.db_table) | ||||||
|  |                     for statement in sql: | ||||||
|  |                         cursor.execute(statement) | ||||||
|  |                     tables.append(connection.introspection.table_name_converter(model._meta.db_table)) | ||||||
|  |  | ||||||
|  |         # Send the post_syncdb signal, so individual apps can do whatever they need | ||||||
|  |         # to do at this point. | ||||||
|  |         emit_post_sync_signal(created_models, self.verbosity, self.interactive, connection.alias) | ||||||
|  |  | ||||||
|  |         # The connection may have been closed by a syncdb handler. | ||||||
|  |         cursor = connection.cursor() | ||||||
|  |  | ||||||
|  |         # Install custom SQL for the app (but only if this | ||||||
|  |         # is a model we've just created) | ||||||
|  |         if self.verbosity >= 1: | ||||||
|  |             self.stdout.write("  Installing custom SQL...\n") | ||||||
|  |         for app_name, model_list in manifest.items(): | ||||||
|  |             for model in model_list: | ||||||
|  |                 if model in created_models: | ||||||
|  |                     custom_sql = custom_sql_for_model(model, self.style, connection) | ||||||
|  |                     if custom_sql: | ||||||
|  |                         if self.verbosity >= 2: | ||||||
|  |                             self.stdout.write("    Installing custom SQL for %s.%s model\n" % (app_name, model._meta.object_name)) | ||||||
|  |                         try: | ||||||
|  |                             with transaction.commit_on_success_unless_managed(using=connection.alias): | ||||||
|  |                                 for sql in custom_sql: | ||||||
|  |                                     cursor.execute(sql) | ||||||
|  |                         except Exception as e: | ||||||
|  |                             self.stderr.write("    Failed to install custom SQL for %s.%s model: %s\n" % (app_name, model._meta.object_name, e)) | ||||||
|  |                             if self.show_traceback: | ||||||
|  |                                 traceback.print_exc() | ||||||
|  |                     else: | ||||||
|  |                         if self.verbosity >= 3: | ||||||
|  |                             self.stdout.write("    No custom SQL for %s.%s model\n" % (app_name, model._meta.object_name)) | ||||||
|  |  | ||||||
|  |         if self.verbosity >= 1: | ||||||
|  |             self.stdout.write("  Installing indexes...\n") | ||||||
|  |         # Install SQL indices for all newly created models | ||||||
|  |         for app_name, model_list in manifest.items(): | ||||||
|  |             for model in model_list: | ||||||
|  |                 if model in created_models: | ||||||
|  |                     index_sql = connection.creation.sql_indexes_for_model(model, self.style) | ||||||
|  |                     if index_sql: | ||||||
|  |                         if self.verbosity >= 2: | ||||||
|  |                             self.stdout.write("    Installing index for %s.%s model\n" % (app_name, model._meta.object_name)) | ||||||
|  |                         try: | ||||||
|  |                             with transaction.commit_on_success_unless_managed(using=connection.alias): | ||||||
|  |                                 for sql in index_sql: | ||||||
|  |                                     cursor.execute(sql) | ||||||
|  |                         except Exception as e: | ||||||
|  |                             self.stderr.write("    Failed to install index for %s.%s model: %s\n" % (app_name, model._meta.object_name, e)) | ||||||
|  |  | ||||||
|  |         # Load initial_data fixtures (unless that has been disabled) | ||||||
|  |         if self.load_initial_data: | ||||||
|  |             call_command('loaddata', 'initial_data', verbosity=self.verbosity, database=connection.alias, skip_validation=True) | ||||||
| @@ -1,15 +1,8 @@ | |||||||
|  | import warnings | ||||||
| from optparse import make_option | from optparse import make_option | ||||||
| import itertools | from django.db import DEFAULT_DB_ALIAS | ||||||
| import traceback |  | ||||||
|  |  | ||||||
| from django.conf import settings |  | ||||||
| from django.core.management import call_command | from django.core.management import call_command | ||||||
| from django.core.management.base import NoArgsCommand | from django.core.management.base import NoArgsCommand | ||||||
| from django.core.management.color import no_style |  | ||||||
| from django.core.management.sql import custom_sql_for_model, emit_post_sync_signal, emit_pre_sync_signal |  | ||||||
| from django.db import connections, router, transaction, models, DEFAULT_DB_ALIAS |  | ||||||
| from django.utils.datastructures import SortedDict |  | ||||||
| from django.utils.importlib import import_module |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class Command(NoArgsCommand): | class Command(NoArgsCommand): | ||||||
| @@ -22,143 +15,8 @@ class Command(NoArgsCommand): | |||||||
|             default=DEFAULT_DB_ALIAS, help='Nominates a database to synchronize. ' |             default=DEFAULT_DB_ALIAS, help='Nominates a database to synchronize. ' | ||||||
|                 'Defaults to the "default" database.'), |                 'Defaults to the "default" database.'), | ||||||
|     ) |     ) | ||||||
|     help = "Create the database tables for all apps in INSTALLED_APPS whose tables haven't already been created." |     help = "Deprecated - use 'migrate' instead." | ||||||
|  |  | ||||||
|     def handle_noargs(self, **options): |     def handle_noargs(self, **options): | ||||||
|  |         warnings.warn("The syncdb command will be removed in Django 1.9", PendingDeprecationWarning) | ||||||
|         verbosity = int(options.get('verbosity')) |         call_command("migrate", **options) | ||||||
|         interactive = options.get('interactive') |  | ||||||
|         show_traceback = options.get('traceback') |  | ||||||
|         load_initial_data = options.get('load_initial_data') |  | ||||||
|  |  | ||||||
|         self.style = no_style() |  | ||||||
|  |  | ||||||
|         # Import the 'management' module within each installed app, to register |  | ||||||
|         # dispatcher events. |  | ||||||
|         for app_name in settings.INSTALLED_APPS: |  | ||||||
|             try: |  | ||||||
|                 import_module('.management', app_name) |  | ||||||
|             except ImportError as exc: |  | ||||||
|                 # This is slightly hackish. We want to ignore ImportErrors |  | ||||||
|                 # if the "management" module itself is missing -- but we don't |  | ||||||
|                 # want to ignore the exception if the management module exists |  | ||||||
|                 # but raises an ImportError for some reason. The only way we |  | ||||||
|                 # can do this is to check the text of the exception. Note that |  | ||||||
|                 # we're a bit broad in how we check the text, because different |  | ||||||
|                 # Python implementations may not use the same text. |  | ||||||
|                 # CPython uses the text "No module named management" |  | ||||||
|                 # PyPy uses "No module named myproject.myapp.management" |  | ||||||
|                 msg = exc.args[0] |  | ||||||
|                 if not msg.startswith('No module named') or 'management' not in msg: |  | ||||||
|                     raise |  | ||||||
|  |  | ||||||
|         db = options.get('database') |  | ||||||
|         connection = connections[db] |  | ||||||
|         cursor = connection.cursor() |  | ||||||
|  |  | ||||||
|         # Get a list of already installed *models* so that references work right. |  | ||||||
|         tables = connection.introspection.table_names() |  | ||||||
|         seen_models = connection.introspection.installed_models(tables) |  | ||||||
|         created_models = set() |  | ||||||
|         pending_references = {} |  | ||||||
|  |  | ||||||
|         # Build the manifest of apps and models that are to be synchronized |  | ||||||
|         all_models = [ |  | ||||||
|             (app.__name__.split('.')[-2], |  | ||||||
|                 [m for m in models.get_models(app, include_auto_created=True) |  | ||||||
|                 if router.allow_syncdb(db, m)]) |  | ||||||
|             for app in models.get_apps() |  | ||||||
|         ] |  | ||||||
|  |  | ||||||
|         def model_installed(model): |  | ||||||
|             opts = model._meta |  | ||||||
|             converter = connection.introspection.table_name_converter |  | ||||||
|             # Note that if a model is unmanaged we short-circuit and never try to install it |  | ||||||
|             return not ((converter(opts.db_table) in tables) or |  | ||||||
|                 (opts.auto_created and converter(opts.auto_created._meta.db_table) in tables)) |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         manifest = SortedDict( |  | ||||||
|             (app_name, list(filter(model_installed, model_list))) |  | ||||||
|             for app_name, model_list in all_models |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         create_models = set([x for x in itertools.chain(*manifest.values())]) |  | ||||||
|         emit_pre_sync_signal(create_models, verbosity, interactive, db) |  | ||||||
|  |  | ||||||
|         # Create the tables for each model |  | ||||||
|         if verbosity >= 1: |  | ||||||
|             self.stdout.write("Creating tables ...\n") |  | ||||||
|         with transaction.commit_on_success_unless_managed(using=db): |  | ||||||
|             for app_name, model_list in manifest.items(): |  | ||||||
|                 for model in model_list: |  | ||||||
|                     # Create the model's database table, if it doesn't already exist. |  | ||||||
|                     if verbosity >= 3: |  | ||||||
|                         self.stdout.write("Processing %s.%s model\n" % (app_name, model._meta.object_name)) |  | ||||||
|                     sql, references = connection.creation.sql_create_model(model, self.style, seen_models) |  | ||||||
|                     seen_models.add(model) |  | ||||||
|                     created_models.add(model) |  | ||||||
|                     for refto, refs in references.items(): |  | ||||||
|                         pending_references.setdefault(refto, []).extend(refs) |  | ||||||
|                         if refto in seen_models: |  | ||||||
|                             sql.extend(connection.creation.sql_for_pending_references(refto, self.style, pending_references)) |  | ||||||
|                     sql.extend(connection.creation.sql_for_pending_references(model, self.style, pending_references)) |  | ||||||
|                     if verbosity >= 1 and sql: |  | ||||||
|                         self.stdout.write("Creating table %s\n" % model._meta.db_table) |  | ||||||
|                     for statement in sql: |  | ||||||
|                         cursor.execute(statement) |  | ||||||
|                     tables.append(connection.introspection.table_name_converter(model._meta.db_table)) |  | ||||||
|  |  | ||||||
|         # Send the post_syncdb signal, so individual apps can do whatever they need |  | ||||||
|         # to do at this point. |  | ||||||
|         emit_post_sync_signal(created_models, verbosity, interactive, db) |  | ||||||
|  |  | ||||||
|         # The connection may have been closed by a syncdb handler. |  | ||||||
|         cursor = connection.cursor() |  | ||||||
|  |  | ||||||
|         # Install custom SQL for the app (but only if this |  | ||||||
|         # is a model we've just created) |  | ||||||
|         if verbosity >= 1: |  | ||||||
|             self.stdout.write("Installing custom SQL ...\n") |  | ||||||
|         for app_name, model_list in manifest.items(): |  | ||||||
|             for model in model_list: |  | ||||||
|                 if model in created_models: |  | ||||||
|                     custom_sql = custom_sql_for_model(model, self.style, connection) |  | ||||||
|                     if custom_sql: |  | ||||||
|                         if verbosity >= 2: |  | ||||||
|                             self.stdout.write("Installing custom SQL for %s.%s model\n" % (app_name, model._meta.object_name)) |  | ||||||
|                         try: |  | ||||||
|                             with transaction.commit_on_success_unless_managed(using=db): |  | ||||||
|                                 for sql in custom_sql: |  | ||||||
|                                     cursor.execute(sql) |  | ||||||
|                         except Exception as e: |  | ||||||
|                             self.stderr.write("Failed to install custom SQL for %s.%s model: %s\n" % \ |  | ||||||
|                                                 (app_name, model._meta.object_name, e)) |  | ||||||
|                             if show_traceback: |  | ||||||
|                                 traceback.print_exc() |  | ||||||
|                     else: |  | ||||||
|                         if verbosity >= 3: |  | ||||||
|                             self.stdout.write("No custom SQL for %s.%s model\n" % (app_name, model._meta.object_name)) |  | ||||||
|  |  | ||||||
|         if verbosity >= 1: |  | ||||||
|             self.stdout.write("Installing indexes ...\n") |  | ||||||
|         # Install SQL indices for all newly created models |  | ||||||
|         for app_name, model_list in manifest.items(): |  | ||||||
|             for model in model_list: |  | ||||||
|                 if model in created_models: |  | ||||||
|                     index_sql = connection.creation.sql_indexes_for_model(model, self.style) |  | ||||||
|                     if index_sql: |  | ||||||
|                         if verbosity >= 2: |  | ||||||
|                             self.stdout.write("Installing index for %s.%s model\n" % (app_name, model._meta.object_name)) |  | ||||||
|                         try: |  | ||||||
|                             with transaction.commit_on_success_unless_managed(using=db): |  | ||||||
|                                 for sql in index_sql: |  | ||||||
|                                     cursor.execute(sql) |  | ||||||
|                         except Exception as e: |  | ||||||
|                             self.stderr.write("Failed to install index for %s.%s model: %s\n" % \ |  | ||||||
|                                                 (app_name, model._meta.object_name, e)) |  | ||||||
|  |  | ||||||
|         # Load initial_data fixtures (unless that has been disabled) |  | ||||||
|         if load_initial_data: |  | ||||||
|             call_command('loaddata', 'initial_data', verbosity=verbosity, |  | ||||||
|                          database=db, skip_validation=True) |  | ||||||
|   | |||||||
| @@ -8,10 +8,12 @@ class MigrationExecutor(object): | |||||||
|     up or down to a specified set of targets. |     up or down to a specified set of targets. | ||||||
|     """ |     """ | ||||||
|  |  | ||||||
|     def __init__(self, connection): |     def __init__(self, connection, progress_callback=None): | ||||||
|         self.connection = connection |         self.connection = connection | ||||||
|         self.loader = MigrationLoader(self.connection) |         self.loader = MigrationLoader(self.connection) | ||||||
|  |         self.loader.load_disk() | ||||||
|         self.recorder = MigrationRecorder(self.connection) |         self.recorder = MigrationRecorder(self.connection) | ||||||
|  |         self.progress_callback = progress_callback | ||||||
|  |  | ||||||
|     def migration_plan(self, targets): |     def migration_plan(self, targets): | ||||||
|         """ |         """ | ||||||
| @@ -34,11 +36,12 @@ class MigrationExecutor(object): | |||||||
|                         applied.add(migration) |                         applied.add(migration) | ||||||
|         return plan |         return plan | ||||||
|  |  | ||||||
|     def migrate(self, targets): |     def migrate(self, targets, plan=None): | ||||||
|         """ |         """ | ||||||
|         Migrates the database up to the given targets. |         Migrates the database up to the given targets. | ||||||
|         """ |         """ | ||||||
|         plan = self.migration_plan(targets) |         if plan is None: | ||||||
|  |             plan = self.migration_plan(targets) | ||||||
|         for migration, backwards in plan: |         for migration, backwards in plan: | ||||||
|             if not backwards: |             if not backwards: | ||||||
|                 self.apply_migration(migration) |                 self.apply_migration(migration) | ||||||
| @@ -49,16 +52,24 @@ class MigrationExecutor(object): | |||||||
|         """ |         """ | ||||||
|         Runs a migration forwards. |         Runs a migration forwards. | ||||||
|         """ |         """ | ||||||
|  |         if self.progress_callback: | ||||||
|  |             self.progress_callback("apply_start", migration) | ||||||
|         with self.connection.schema_editor() as schema_editor: |         with self.connection.schema_editor() as schema_editor: | ||||||
|             project_state = self.loader.graph.project_state((migration.app_label, migration.name), at_end=False) |             project_state = self.loader.graph.project_state((migration.app_label, migration.name), at_end=False) | ||||||
|             migration.apply(project_state, schema_editor) |             migration.apply(project_state, schema_editor) | ||||||
|         self.recorder.record_applied(migration.app_label, migration.name) |         self.recorder.record_applied(migration.app_label, migration.name) | ||||||
|  |         if self.progress_callback: | ||||||
|  |             self.progress_callback("apply_success", migration) | ||||||
|  |  | ||||||
|     def unapply_migration(self, migration): |     def unapply_migration(self, migration): | ||||||
|         """ |         """ | ||||||
|         Runs a migration backwards. |         Runs a migration backwards. | ||||||
|         """ |         """ | ||||||
|  |         if self.progress_callback: | ||||||
|  |             self.progress_callback("unapply_start", migration) | ||||||
|         with self.connection.schema_editor() as schema_editor: |         with self.connection.schema_editor() as schema_editor: | ||||||
|             project_state = self.loader.graph.project_state((migration.app_label, migration.name), at_end=False) |             project_state = self.loader.graph.project_state((migration.app_label, migration.name), at_end=False) | ||||||
|             migration.unapply(project_state, schema_editor) |             migration.unapply(project_state, schema_editor) | ||||||
|         self.recorder.record_unapplied(migration.app_label, migration.name) |         self.recorder.record_unapplied(migration.app_label, migration.name) | ||||||
|  |         if self.progress_callback: | ||||||
|  |             self.progress_callback("unapply_success", migration) | ||||||
|   | |||||||
| @@ -41,6 +41,7 @@ class MigrationLoader(object): | |||||||
|         Loads the migrations from all INSTALLED_APPS from disk. |         Loads the migrations from all INSTALLED_APPS from disk. | ||||||
|         """ |         """ | ||||||
|         self.disk_migrations = {} |         self.disk_migrations = {} | ||||||
|  |         self.unmigrated_apps = set() | ||||||
|         for app in cache.get_apps(): |         for app in cache.get_apps(): | ||||||
|             # Get the migrations module directory |             # Get the migrations module directory | ||||||
|             module_name = ".".join(app.__name__.split(".")[:-1] + ["migrations"]) |             module_name = ".".join(app.__name__.split(".")[:-1] + ["migrations"]) | ||||||
| @@ -50,7 +51,8 @@ class MigrationLoader(object): | |||||||
|             except ImportError as e: |             except ImportError as e: | ||||||
|                 # I hate doing this, but I don't want to squash other import errors. |                 # I hate doing this, but I don't want to squash other import errors. | ||||||
|                 # Might be better to try a directory check directly. |                 # Might be better to try a directory check directly. | ||||||
|                 if "No module named migrations" in str(e): |                 if "No module named" in str(e) and "migrations" in str(e): | ||||||
|  |                     self.unmigrated_apps.add(app_label) | ||||||
|                     continue |                     continue | ||||||
|             directory = os.path.dirname(module.__file__) |             directory = os.path.dirname(module.__file__) | ||||||
|             # Scan for .py[c|o] files |             # Scan for .py[c|o] files | ||||||
|   | |||||||
| @@ -47,6 +47,9 @@ class Migration(object): | |||||||
|     def __repr__(self): |     def __repr__(self): | ||||||
|         return "<Migration %s.%s>" % (self.app_label, self.name) |         return "<Migration %s.%s>" % (self.app_label, self.name) | ||||||
|  |  | ||||||
|  |     def __str__(self): | ||||||
|  |         return "%s.%s" % (self.app_label, self.name) | ||||||
|  |  | ||||||
|     def __hash__(self): |     def __hash__(self): | ||||||
|         return hash("%s.%s" % (self.app_label, self.name)) |         return hash("%s.%s" % (self.app_label, self.name)) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -86,6 +86,8 @@ PALETTES = { | |||||||
|         'HTTP_BAD_REQUEST':  {}, |         'HTTP_BAD_REQUEST':  {}, | ||||||
|         'HTTP_NOT_FOUND':    {}, |         'HTTP_NOT_FOUND':    {}, | ||||||
|         'HTTP_SERVER_ERROR': {}, |         'HTTP_SERVER_ERROR': {}, | ||||||
|  |         'MIGRATE_HEADING':   {}, | ||||||
|  |         'MIGRATE_LABEL':     {}, | ||||||
|     }, |     }, | ||||||
|     DARK_PALETTE: { |     DARK_PALETTE: { | ||||||
|         'ERROR':        { 'fg': 'red', 'opts': ('bold',) }, |         'ERROR':        { 'fg': 'red', 'opts': ('bold',) }, | ||||||
| @@ -101,6 +103,8 @@ PALETTES = { | |||||||
|         'HTTP_BAD_REQUEST':  { 'fg': 'red', 'opts': ('bold',) }, |         'HTTP_BAD_REQUEST':  { 'fg': 'red', 'opts': ('bold',) }, | ||||||
|         'HTTP_NOT_FOUND':    { 'fg': 'yellow' }, |         'HTTP_NOT_FOUND':    { 'fg': 'yellow' }, | ||||||
|         'HTTP_SERVER_ERROR': { 'fg': 'magenta', 'opts': ('bold',) }, |         'HTTP_SERVER_ERROR': { 'fg': 'magenta', 'opts': ('bold',) }, | ||||||
|  |         'MIGRATE_HEADING':   { 'fg': 'cyan', 'opts': ('bold',) }, | ||||||
|  |         'MIGRATE_LABEL':     { 'opts': ('bold',) }, | ||||||
|     }, |     }, | ||||||
|     LIGHT_PALETTE: { |     LIGHT_PALETTE: { | ||||||
|         'ERROR':        { 'fg': 'red', 'opts': ('bold',) }, |         'ERROR':        { 'fg': 'red', 'opts': ('bold',) }, | ||||||
| @@ -116,6 +120,8 @@ PALETTES = { | |||||||
|         'HTTP_BAD_REQUEST':  { 'fg': 'red', 'opts': ('bold',) }, |         'HTTP_BAD_REQUEST':  { 'fg': 'red', 'opts': ('bold',) }, | ||||||
|         'HTTP_NOT_FOUND':    { 'fg': 'red' }, |         'HTTP_NOT_FOUND':    { 'fg': 'red' }, | ||||||
|         'HTTP_SERVER_ERROR': { 'fg': 'magenta', 'opts': ('bold',) }, |         'HTTP_SERVER_ERROR': { 'fg': 'magenta', 'opts': ('bold',) }, | ||||||
|  |         'MIGRATE_HEADING':   { 'fg': 'cyan', 'opts': ('bold',) }, | ||||||
|  |         'MIGRATE_LABEL':     { 'opts': ('bold',) }, | ||||||
|     } |     } | ||||||
| } | } | ||||||
| DEFAULT_PALETTE = DARK_PALETTE | DEFAULT_PALETTE = DARK_PALETTE | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user