From a338e0773592ce4030407428c77815a713b6c9c7 Mon Sep 17 00:00:00 2001 From: Andrew Godwin Date: Tue, 29 Jul 2014 09:38:08 -0700 Subject: [PATCH] Fixed #23101: Prefer doing deletes before creates in autodetector. Makes declined or missed renames still work (but drop data). --- django/db/migrations/autodetector.py | 26 ++++++++++++-------- tests/migrations/test_autodetector.py | 35 ++++++++------------------- 2 files changed, 26 insertions(+), 35 deletions(-) diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py index e27d8ace74..dd9595bd7e 100644 --- a/django/db/migrations/autodetector.py +++ b/django/db/migrations/autodetector.py @@ -160,15 +160,16 @@ class MigrationAutodetector(object): self.through_users[through_key] = (app_label, old_model_name, field_name) # Generate non-rename model operations - self.generate_created_models() self.generate_deleted_models() - self.generate_created_proxies() + self.generate_created_models() self.generate_deleted_proxies() + self.generate_created_proxies() self.generate_altered_options() # Generate field operations - self.generate_added_fields() + self.generate_renamed_fields() self.generate_removed_fields() + self.generate_added_fields() self.generate_altered_fields() self.generate_altered_unique_together() self.generate_altered_index_together() @@ -682,17 +683,17 @@ class MigrationAutodetector(object): ), ) - def generate_added_fields(self): - # New fields + def generate_renamed_fields(self): + """ + Works out renamed fields + """ self.renamed_fields = {} for app_label, model_name, field_name in sorted(self.new_field_keys - self.old_field_keys): old_model_name = self.renamed_models.get((app_label, model_name), model_name) old_model_state = self.from_state.models[app_label, old_model_name] - new_model_state = self.to_state.models[app_label, model_name] field = self.new_apps.get_model(app_label, model_name)._meta.get_field_by_name(field_name)[0] # Scan to see if this is actually a rename! field_dec = self.deep_deconstruct(field) - found_rename = False for rem_app_label, rem_model_name, rem_field_name in sorted(self.old_field_keys - self.new_field_keys): if rem_app_label == app_label and rem_model_name == model_name: old_field_dec = self.deep_deconstruct(old_model_state.get_field_by_name(rem_field_name)) @@ -713,10 +714,15 @@ class MigrationAutodetector(object): self.old_field_keys.remove((rem_app_label, rem_model_name, rem_field_name)) self.old_field_keys.add((app_label, model_name, field_name)) self.renamed_fields[app_label, model_name, field_name] = rem_field_name - found_rename = True break - if found_rename: - continue + + + def generate_added_fields(self): + """ + Fields that have been added + """ + for app_label, model_name, field_name in sorted(self.new_field_keys - self.old_field_keys): + field = self.new_apps.get_model(app_label, model_name)._meta.get_field_by_name(field_name)[0] # Fields that are foreignkeys/m2ms depend on stuff dependencies = [] if field.rel and field.rel.to: diff --git a/tests/migrations/test_autodetector.py b/tests/migrations/test_autodetector.py index e750fc9194..73e89bbec2 100644 --- a/tests/migrations/test_autodetector.py +++ b/tests/migrations/test_autodetector.py @@ -278,16 +278,10 @@ class AutodetectorTests(TestCase): after = self.make_project_state([self.author_name_renamed]) autodetector = MigrationAutodetector(before, after, MigrationQuestioner({"ask_rename": True})) changes = autodetector._detect_changes() - # Right number of migrations? - self.assertEqual(len(changes['testapp']), 1) - # Right number of actions? - migration = changes['testapp'][0] - self.assertEqual(len(migration.operations), 1) - # Right action? - action = migration.operations[0] - self.assertEqual(action.__class__.__name__, "RenameField") - self.assertEqual(action.old_name, "name") - self.assertEqual(action.new_name, "names") + # Check + self.assertNumberMigrations(changes, 'testapp', 1) + self.assertOperationTypes(changes, 'testapp', 0, ["RenameField"]) + self.assertOperationAttributes(changes, 'testapp', 0, 0, old_name="name", new_name="names") def test_rename_model(self): "Tests autodetection of renamed models" @@ -731,21 +725,12 @@ class AutodetectorTests(TestCase): after = self.make_project_state([self.author_with_publisher, self.publisher]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() - # Right number of migrations? - self.assertEqual(len(changes['testapp']), 1) - # Right number of actions? - migration = changes['testapp'][0] - self.assertEqual(len(migration.operations), 3) - # Right actions? - action = migration.operations[0] - self.assertEqual(action.__class__.__name__, "CreateModel") - self.assertEqual(action.name, "Publisher") - action = migration.operations[1] - self.assertEqual(action.__class__.__name__, "AddField") - self.assertEqual(action.name, "publisher") - action = migration.operations[2] - self.assertEqual(action.__class__.__name__, "RemoveField") - self.assertEqual(action.name, "publisher_name") + # Right result? + self.assertNumberMigrations(changes, 'testapp', 1) + self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel", "RemoveField", "AddField"]) + self.assertOperationAttributes(changes, 'testapp', 0, 0, name="Publisher") + self.assertOperationAttributes(changes, 'testapp', 0, 1, name="publisher_name") + self.assertOperationAttributes(changes, 'testapp', 0, 2, name="publisher") def test_foreign_key_removed_before_target_model(self): """