浏览代码

Fixed #23629 -- Allowed autodetector to detect changes in Meta.db_table.

Thanks Naddiseo for reporting.
Tianyi Wang 10 年之前
父节点
当前提交
5732424bee
共有 3 个文件被更改,包括 111 次插入1 次删除
  1. 18 0
      django/db/migrations/autodetector.py
  2. 3 0
      docs/releases/1.7.1.txt
  3. 90 1
      tests/migrations/test_autodetector.py

+ 18 - 0
django/db/migrations/autodetector.py

@@ -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
@@ -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

+ 3 - 0
docs/releases/1.7.1.txt

@@ -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`).

+ 90 - 1
tests/migrations/test_autodetector.py

@@ -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))])
@@ -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?
@@ -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