1
0
mirror of https://github.com/django/django.git synced 2025-06-01 09:39:12 +00:00

Fixed #23121: AlterModelOptions operation not changing state right

This commit is contained in:
Andrew Godwin 2014-07-28 10:47:28 -07:00
parent cb60d22bd9
commit d6e73a876d
4 changed files with 45 additions and 18 deletions

View File

@ -10,6 +10,7 @@ from django.db.migrations import operations
from django.db.migrations.migration import Migration from django.db.migrations.migration import Migration
from django.db.migrations.questioner import MigrationQuestioner from django.db.migrations.questioner import MigrationQuestioner
from django.db.migrations.optimizer import MigrationOptimizer from django.db.migrations.optimizer import MigrationOptimizer
from django.db.migrations.operations.models import AlterModelOptions
class MigrationAutodetector(object): class MigrationAutodetector(object):
@ -25,17 +26,6 @@ class MigrationAutodetector(object):
if it wishes, with the caveat that it may not always be possible. if it wishes, with the caveat that it may not always be possible.
""" """
# Model options we want to compare and preserve in an AlterModelOptions op
ALTER_OPTION_KEYS = [
"get_latest_by",
"ordering",
"permissions",
"default_permissions",
"select_on_save",
"verbose_name",
"verbose_name_plural",
]
def __init__(self, from_state, to_state, questioner=None): def __init__(self, from_state, to_state, questioner=None):
self.from_state = from_state self.from_state = from_state
self.to_state = to_state self.to_state = to_state
@ -864,11 +854,11 @@ class MigrationAutodetector(object):
new_model_state = self.to_state.models[app_label, model_name] new_model_state = self.to_state.models[app_label, model_name]
old_options = dict( old_options = dict(
option for option in old_model_state.options.items() option for option in old_model_state.options.items()
if option[0] in self.ALTER_OPTION_KEYS if option[0] in AlterModelOptions.ALTER_OPTION_KEYS
) )
new_options = dict( new_options = dict(
option for option in new_model_state.options.items() option for option in new_model_state.options.items()
if option[0] in self.ALTER_OPTION_KEYS if option[0] in AlterModelOptions.ALTER_OPTION_KEYS
) )
if old_options != new_options: if old_options != new_options:
self.add_operation( self.add_operation(

View File

@ -337,6 +337,17 @@ class AlterModelOptions(Operation):
may still need them. may still need them.
""" """
# Model options we want to compare and preserve in an AlterModelOptions op
ALTER_OPTION_KEYS = [
"get_latest_by",
"ordering",
"permissions",
"default_permissions",
"select_on_save",
"verbose_name",
"verbose_name_plural",
]
def __init__(self, name, options): def __init__(self, name, options):
self.name = name self.name = name
self.options = options self.options = options
@ -345,6 +356,9 @@ class AlterModelOptions(Operation):
model_state = state.models[app_label, self.name.lower()] model_state = state.models[app_label, self.name.lower()]
model_state.options = dict(model_state.options) model_state.options = dict(model_state.options)
model_state.options.update(self.options) model_state.options.update(self.options)
for key in self.ALTER_OPTION_KEYS:
if key not in self.options and key in model_state.options:
del model_state.options[key]
def database_forwards(self, app_label, schema_editor, from_state, to_state): def database_forwards(self, app_label, schema_editor, from_state, to_state):
pass pass

View File

@ -897,6 +897,13 @@ class AutodetectorTests(TestCase):
self.assertNumberMigrations(changes, "testapp", 1) self.assertNumberMigrations(changes, "testapp", 1)
# Right actions in right order? # Right actions in right order?
self.assertOperationTypes(changes, "testapp", 0, ["AlterModelOptions"]) self.assertOperationTypes(changes, "testapp", 0, ["AlterModelOptions"])
# Changing them back to empty should also make a change
before = self.make_project_state([self.author_with_options])
after = self.make_project_state([self.author_empty])
autodetector = MigrationAutodetector(before, after)
changes = autodetector._detect_changes()
self.assertNumberMigrations(changes, "testapp", 1)
self.assertOperationTypes(changes, "testapp", 0, ["AlterModelOptions"])
def test_alter_model_options_proxy(self): def test_alter_model_options_proxy(self):
""" """

View File

@ -46,7 +46,7 @@ class OperationTestBase(MigrationTestBase):
operation.state_forwards(app_label, new_state) operation.state_forwards(app_label, new_state)
return project_state, new_state return project_state, new_state
def set_up_test_model(self, app_label, second_model=False, third_model=False, related_model=False, mti_model=False, proxy_model=False, unique_together=False): def set_up_test_model(self, app_label, second_model=False, third_model=False, related_model=False, mti_model=False, proxy_model=False, unique_together=False, options=False):
""" """
Creates a test model state and database table. Creates a test model state and database table.
""" """
@ -76,6 +76,12 @@ class OperationTestBase(MigrationTestBase):
except DatabaseError: except DatabaseError:
pass pass
# Make the "current" state # Make the "current" state
model_options = {
"swappable": "TEST_SWAP_MODEL",
"unique_together": [["pink", "weight"]] if unique_together else [],
}
if options:
model_options["permissions"] = [("can_groom", "Can groom")]
operations = [migrations.CreateModel( operations = [migrations.CreateModel(
"Pony", "Pony",
[ [
@ -83,10 +89,7 @@ class OperationTestBase(MigrationTestBase):
("pink", models.IntegerField(default=3)), ("pink", models.IntegerField(default=3)),
("weight", models.FloatField()), ("weight", models.FloatField()),
], ],
options={ options=model_options,
"swappable": "TEST_SWAP_MODEL",
"unique_together": [["pink", "weight"]] if unique_together else [],
},
)] )]
if second_model: if second_model:
operations.append(migrations.CreateModel( operations.append(migrations.CreateModel(
@ -975,6 +978,19 @@ class OperationTests(OperationTestBase):
self.assertEqual(len(new_state.models["test_almoop", "pony"].options.get("permissions", [])), 1) self.assertEqual(len(new_state.models["test_almoop", "pony"].options.get("permissions", [])), 1)
self.assertEqual(new_state.models["test_almoop", "pony"].options["permissions"][0][0], "can_groom") self.assertEqual(new_state.models["test_almoop", "pony"].options["permissions"][0][0], "can_groom")
def test_alter_model_options_emptying(self):
"""
Tests that the AlterModelOptions operation removes keys from the dict (#23121)
"""
project_state = self.set_up_test_model("test_almoop", options=True)
# Test the state alteration (no DB alteration to test)
operation = migrations.AlterModelOptions("Pony", {})
self.assertEqual(operation.describe(), "Change Meta options on Pony")
new_state = project_state.clone()
operation.state_forwards("test_almoop", new_state)
self.assertEqual(len(project_state.models["test_almoop", "pony"].options.get("permissions", [])), 1)
self.assertEqual(len(new_state.models["test_almoop", "pony"].options.get("permissions", [])), 0)
def test_alter_order_with_respect_to(self): def test_alter_order_with_respect_to(self):
""" """
Tests the AlterOrderWithRespectTo operation. Tests the AlterOrderWithRespectTo operation.