diff --git a/django/core/management/commands/migrate.py b/django/core/management/commands/migrate.py index 0f12508559..a07e3e0d68 100644 --- a/django/core/management/commands/migrate.py +++ b/django/core/management/commands/migrate.py @@ -72,7 +72,7 @@ class Command(NoArgsCommand): 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)")) + self.stdout.write(self.style.MIGRATE_LABEL(" Apps with migrations: ") + (", ".join(executor.loader.migrated_apps) or "(none)")) # Run the syncdb phase. # If you ever manage to get rid of this, I owe you many, many drinks. diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py index 79f710482d..213516f908 100644 --- a/django/db/migrations/autodetector.py +++ b/django/db/migrations/autodetector.py @@ -34,7 +34,7 @@ class MigrationAutodetector(object): """ # We'll store migrations as lists by app names for now self.migrations = {} - # Stage one: Adding models. + # Adding models. added_models = set(self.to_state.models.keys()) - set(self.from_state.models.keys()) for app_label, model_name in added_models: model_state = self.to_state.models[app_label, model_name] @@ -57,6 +57,32 @@ class MigrationAutodetector(object): model_state.name, ) ) + # Changes within models + kept_models = set(self.from_state.models.keys()).intersection(self.to_state.models.keys()) + for app_label, model_name in kept_models: + old_model_state = self.from_state.models[app_label, model_name] + new_model_state = self.to_state.models[app_label, model_name] + # New fields + old_field_names = set([x for x, y in old_model_state.fields]) + new_field_names = set([x for x, y in new_model_state.fields]) + for field_name in new_field_names - old_field_names: + self.add_to_migration( + app_label, + operations.AddField( + model_name = model_name, + name = field_name, + field = [y for x, y in new_model_state.fields if x == field_name][0], + ) + ) + # Old fields + for field_name in old_field_names - new_field_names: + self.add_to_migration( + app_label, + operations.RemoveField( + model_name = model_name, + name = field_name, + ) + ) # Alright, now add internal dependencies for app_label, migrations in self.migrations.items(): for m1, m2 in zip(migrations, migrations[1:]): diff --git a/django/db/migrations/loader.py b/django/db/migrations/loader.py index 9658793094..36e1540299 100644 --- a/django/db/migrations/loader.py +++ b/django/db/migrations/loader.py @@ -50,6 +50,7 @@ class MigrationLoader(object): """ self.disk_migrations = {} self.unmigrated_apps = set() + self.migrated_apps = set() for app in cache.get_apps(): # Get the migrations module directory app_label = app.__name__.split(".")[-2] @@ -62,6 +63,7 @@ class MigrationLoader(object): if "No module named" in str(e) and "migrations" in str(e): self.unmigrated_apps.add(app_label) continue + self.migrated_apps.add(app_label) directory = os.path.dirname(module.__file__) # Scan for .py[c|o] files migration_names = set() diff --git a/django/db/migrations/operations/fields.py b/django/db/migrations/operations/fields.py index 8fd8c9151d..660cba6b72 100644 --- a/django/db/migrations/operations/fields.py +++ b/django/db/migrations/operations/fields.py @@ -6,13 +6,13 @@ class AddField(Operation): Adds a field to a model. """ - def __init__(self, model_name, name, instance): + def __init__(self, model_name, name, field): self.model_name = model_name self.name = name - self.instance = instance + self.field = field def state_forwards(self, app_label, state): - state.models[app_label, self.model_name.lower()].fields.append((self.name, self.instance)) + state.models[app_label, self.model_name.lower()].fields.append((self.name, self.field)) def database_forwards(self, app_label, schema_editor, from_state, to_state): from_model = from_state.render().get_model(app_label, self.model_name)