summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTianyi Wang <wty52133@gmail.com>2014-10-14 15:20:24 +0100
committerTim Graham <timograham@gmail.com>2014-10-20 13:15:34 -0400
commit3f3bed8fb9109783aaa15a700a811255b8640cea (patch)
tree2726a85836f88c1d9df4f873ebebfa0ba3921d30
parent417923c69ee93f1737517156b13beaefc81f1e0e (diff)
[1.7.x] Fixed #23629 -- Allowed autodetector to detect changes in Meta.db_table.
Thanks Naddiseo for reporting. Backport of 5732424bee from master
-rw-r--r--django/db/migrations/autodetector.py18
-rw-r--r--docs/releases/1.7.1.txt3
-rw-r--r--tests/migrations/test_autodetector.py91
3 files changed, 111 insertions, 1 deletions
diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py
index e9f3cde539..a8bd560e77 100644
--- a/django/db/migrations/autodetector.py
+++ b/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
diff --git a/docs/releases/1.7.1.txt b/docs/releases/1.7.1.txt
index 65f70f69a1..dfb1acbeac 100644
--- a/docs/releases/1.7.1.txt
+++ b/docs/releases/1.7.1.txt
@@ -125,3 +125,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`).
diff --git a/tests/migrations/test_autodetector.py b/tests/migrations/test_autodetector.py
index 56596309be..f80ad0b7a3 100644
--- a/tests/migrations/test_autodetector.py
+++ b/tests/migrations/test_autodetector.py
@@ -56,6 +56,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))])
@@ -370,7 +379,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?
@@ -582,6 +590,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