Loading django/db/migrations/autodetector.py +18 −0 Original line number Diff line number Diff line Loading @@ -183,6 +183,7 @@ class MigrationAutodetector(object): self.generate_altered_fields() self.generate_altered_unique_together() self.generate_altered_index_together() self.generate_altered_db_table() self.generate_altered_order_with_respect_to() # Now, reordering to make things possible. The order we have already Loading Loading @@ -910,6 +911,23 @@ class MigrationAutodetector(object): def generate_altered_index_together(self): self._generate_altered_foo_together(operations.AlterIndexTogether) def generate_altered_db_table(self): models_to_check = self.kept_model_keys.union(self.kept_proxy_keys).union(self.kept_unmanaged_keys) for app_label, model_name in sorted(models_to_check): 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] old_db_table_name = old_model_state.options.get('db_table') new_db_table_name = new_model_state.options.get('db_table') if old_db_table_name != new_db_table_name: self.add_operation( app_label, operations.AlterModelTable( name=model_name, table=new_db_table_name, ) ) def generate_altered_options(self): """ Works out if any non-schema-affecting options have changed and Loading docs/releases/1.7.1.txt +3 −0 Original line number Diff line number Diff line Loading @@ -126,3 +126,6 @@ Bugfixes * Made the Oracle test database creation drop the test user in the event of an unclean exit of a previous test run (:ticket:`23649`). * Fixed :djadmin:`makemigrations` to detect changes to :attr:`Meta.db_table <django.db.models.Options.db_table>` (:ticket:`23629`). tests/migrations/test_autodetector.py +90 −1 Original line number Diff line number Diff line Loading @@ -58,6 +58,15 @@ class AutodetectorTests(TestCase): ]) author_with_m2m_through = ModelState("testapp", "Author", [("id", models.AutoField(primary_key=True)), ("publishers", models.ManyToManyField("testapp.Publisher", through="testapp.Contract"))]) author_with_options = ModelState("testapp", "Author", [("id", models.AutoField(primary_key=True))], {"verbose_name": "Authi", "permissions": [('can_hire', 'Can hire')]}) author_with_db_table_options = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)) ], {"db_table": "author_one"}) author_with_new_db_table_options = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)) ], {"db_table": "author_two"}) author_renamed_with_db_table_options = ModelState("testapp", "NewAuthor", [ ("id", models.AutoField(primary_key=True)) ], {"db_table": "author_one"}) contract = ModelState("testapp", "Contract", [("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("testapp.Author")), ("publisher", models.ForeignKey("testapp.Publisher"))]) publisher = ModelState("testapp", "Publisher", [("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=100))]) publisher_with_author = ModelState("testapp", "Publisher", [("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("testapp.Author")), ("name", models.CharField(max_length=100))]) Loading Loading @@ -397,7 +406,6 @@ class AutodetectorTests(TestCase): after = self.make_project_state([self.author_renamed_with_book, self.book_with_author_renamed]) autodetector = MigrationAutodetector(before, after, MigrationQuestioner({"ask_rename_model": True})) changes = autodetector._detect_changes() # Right number of migrations for model rename? self.assertNumberMigrations(changes, 'testapp', 1) # Right number of actions? Loading Loading @@ -609,6 +617,87 @@ class AutodetectorTests(TestCase): # Right number of migrations? self.assertEqual(len(changes), 0) def test_alter_db_table_add(self): """Tests detection for adding db_table in model's options""" # Make state before = self.make_project_state([self.author_empty]) after = self.make_project_state([self.author_with_db_table_options]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number of migrations? self.assertEqual(len(changes), 1) # Right number of actions? migration = changes['testapp'][0] self.assertEqual(len(migration.operations), 1) action = migration.operations[0] self.assertEqual(action.__class__.__name__, "AlterModelTable") self.assertEqual(action.name, "author") self.assertEqual(action.table, "author_one") def test_alter_db_table_change(self): "Tests detection for changing db_table in model's options'" # Make state before = self.make_project_state([self.author_with_db_table_options]) after = self.make_project_state([self.author_with_new_db_table_options]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number of migrations? self.assertEqual(len(changes), 1) # Right number of actions? migration = changes['testapp'][0] self.assertEqual(len(migration.operations), 1) action = migration.operations[0] self.assertEqual(action.__class__.__name__, "AlterModelTable") self.assertEqual(action.name, "author") self.assertEqual(action.table, "author_two") def test_alter_db_table_remove(self): """Tests detection for removing db_table in model's options""" # Make state before = self.make_project_state([self.author_with_db_table_options]) after = self.make_project_state([self.author_empty]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number of migrations? self.assertEqual(len(changes), 1) # Right number of actions? migration = changes['testapp'][0] self.assertEqual(len(migration.operations), 1) action = migration.operations[0] self.assertEqual(action.__class__.__name__, "AlterModelTable") self.assertEqual(action.name, "author") self.assertEqual(action.table, None) def test_alter_db_table_no_changes(self): """ Tests that alter_db_table doesn't generate a migration if no changes have been made. """ # Make state before = self.make_project_state([self.author_with_db_table_options]) after = self.make_project_state([self.author_with_db_table_options]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number of migrations? self.assertEqual(len(changes), 0) def test_alter_db_table_with_model_change(self): """ Tests when model changes, autodetector does not create more than one operation. """ # Make state before = self.make_project_state([self.author_with_db_table_options]) after = self.make_project_state([self.author_renamed_with_db_table_options]) autodetector = MigrationAutodetector( before, after, MigrationQuestioner({"ask_rename_model": True}) ) changes = autodetector._detect_changes() # Right number of migrations? self.assertEqual(len(changes), 1) migration = changes['testapp'][0] self.assertEqual(len(migration.operations), 1) def test_empty_foo_together(self): "#23452 - Empty unique/index_togther shouldn't generate a migration." # Explicitly testing for not specified, since this is the case after Loading Loading
django/db/migrations/autodetector.py +18 −0 Original line number Diff line number Diff line Loading @@ -183,6 +183,7 @@ class MigrationAutodetector(object): self.generate_altered_fields() self.generate_altered_unique_together() self.generate_altered_index_together() self.generate_altered_db_table() self.generate_altered_order_with_respect_to() # Now, reordering to make things possible. The order we have already Loading Loading @@ -910,6 +911,23 @@ class MigrationAutodetector(object): def generate_altered_index_together(self): self._generate_altered_foo_together(operations.AlterIndexTogether) def generate_altered_db_table(self): models_to_check = self.kept_model_keys.union(self.kept_proxy_keys).union(self.kept_unmanaged_keys) for app_label, model_name in sorted(models_to_check): 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] old_db_table_name = old_model_state.options.get('db_table') new_db_table_name = new_model_state.options.get('db_table') if old_db_table_name != new_db_table_name: self.add_operation( app_label, operations.AlterModelTable( name=model_name, table=new_db_table_name, ) ) def generate_altered_options(self): """ Works out if any non-schema-affecting options have changed and Loading
docs/releases/1.7.1.txt +3 −0 Original line number Diff line number Diff line Loading @@ -126,3 +126,6 @@ Bugfixes * Made the Oracle test database creation drop the test user in the event of an unclean exit of a previous test run (:ticket:`23649`). * Fixed :djadmin:`makemigrations` to detect changes to :attr:`Meta.db_table <django.db.models.Options.db_table>` (:ticket:`23629`).
tests/migrations/test_autodetector.py +90 −1 Original line number Diff line number Diff line Loading @@ -58,6 +58,15 @@ class AutodetectorTests(TestCase): ]) author_with_m2m_through = ModelState("testapp", "Author", [("id", models.AutoField(primary_key=True)), ("publishers", models.ManyToManyField("testapp.Publisher", through="testapp.Contract"))]) author_with_options = ModelState("testapp", "Author", [("id", models.AutoField(primary_key=True))], {"verbose_name": "Authi", "permissions": [('can_hire', 'Can hire')]}) author_with_db_table_options = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)) ], {"db_table": "author_one"}) author_with_new_db_table_options = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)) ], {"db_table": "author_two"}) author_renamed_with_db_table_options = ModelState("testapp", "NewAuthor", [ ("id", models.AutoField(primary_key=True)) ], {"db_table": "author_one"}) contract = ModelState("testapp", "Contract", [("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("testapp.Author")), ("publisher", models.ForeignKey("testapp.Publisher"))]) publisher = ModelState("testapp", "Publisher", [("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=100))]) publisher_with_author = ModelState("testapp", "Publisher", [("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("testapp.Author")), ("name", models.CharField(max_length=100))]) Loading Loading @@ -397,7 +406,6 @@ class AutodetectorTests(TestCase): after = self.make_project_state([self.author_renamed_with_book, self.book_with_author_renamed]) autodetector = MigrationAutodetector(before, after, MigrationQuestioner({"ask_rename_model": True})) changes = autodetector._detect_changes() # Right number of migrations for model rename? self.assertNumberMigrations(changes, 'testapp', 1) # Right number of actions? Loading Loading @@ -609,6 +617,87 @@ class AutodetectorTests(TestCase): # Right number of migrations? self.assertEqual(len(changes), 0) def test_alter_db_table_add(self): """Tests detection for adding db_table in model's options""" # Make state before = self.make_project_state([self.author_empty]) after = self.make_project_state([self.author_with_db_table_options]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number of migrations? self.assertEqual(len(changes), 1) # Right number of actions? migration = changes['testapp'][0] self.assertEqual(len(migration.operations), 1) action = migration.operations[0] self.assertEqual(action.__class__.__name__, "AlterModelTable") self.assertEqual(action.name, "author") self.assertEqual(action.table, "author_one") def test_alter_db_table_change(self): "Tests detection for changing db_table in model's options'" # Make state before = self.make_project_state([self.author_with_db_table_options]) after = self.make_project_state([self.author_with_new_db_table_options]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number of migrations? self.assertEqual(len(changes), 1) # Right number of actions? migration = changes['testapp'][0] self.assertEqual(len(migration.operations), 1) action = migration.operations[0] self.assertEqual(action.__class__.__name__, "AlterModelTable") self.assertEqual(action.name, "author") self.assertEqual(action.table, "author_two") def test_alter_db_table_remove(self): """Tests detection for removing db_table in model's options""" # Make state before = self.make_project_state([self.author_with_db_table_options]) after = self.make_project_state([self.author_empty]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number of migrations? self.assertEqual(len(changes), 1) # Right number of actions? migration = changes['testapp'][0] self.assertEqual(len(migration.operations), 1) action = migration.operations[0] self.assertEqual(action.__class__.__name__, "AlterModelTable") self.assertEqual(action.name, "author") self.assertEqual(action.table, None) def test_alter_db_table_no_changes(self): """ Tests that alter_db_table doesn't generate a migration if no changes have been made. """ # Make state before = self.make_project_state([self.author_with_db_table_options]) after = self.make_project_state([self.author_with_db_table_options]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number of migrations? self.assertEqual(len(changes), 0) def test_alter_db_table_with_model_change(self): """ Tests when model changes, autodetector does not create more than one operation. """ # Make state before = self.make_project_state([self.author_with_db_table_options]) after = self.make_project_state([self.author_renamed_with_db_table_options]) autodetector = MigrationAutodetector( before, after, MigrationQuestioner({"ask_rename_model": True}) ) changes = autodetector._detect_changes() # Right number of migrations? self.assertEqual(len(changes), 1) migration = changes['testapp'][0] self.assertEqual(len(migration.operations), 1) def test_empty_foo_together(self): "#23452 - Empty unique/index_togther shouldn't generate a migration." # Explicitly testing for not specified, since this is the case after Loading