Fixed #28350 -- Fixed UnboundLocalError crash in RenameField with nonexistent field.

Thanks Tim for the review.
This commit is contained in:
Simon Charette 2017-06-30 08:35:58 -04:00
parent b9f7dce84b
commit 5cbcb36839
3 changed files with 24 additions and 11 deletions

View File

@ -1,3 +1,4 @@
from django.core.exceptions import FieldDoesNotExist
from django.db.models.fields import NOT_PROVIDED from django.db.models.fields import NOT_PROVIDED
from django.utils.functional import cached_property from django.utils.functional import cached_property
@ -261,25 +262,27 @@ class RenameField(FieldOperation):
) )
def state_forwards(self, app_label, state): def state_forwards(self, app_label, state):
model_state = state.models[app_label, self.model_name_lower]
# Rename the field # Rename the field
state.models[app_label, self.model_name_lower].fields = [ fields = model_state.fields
(self.new_name if n == self.old_name else n, f) for index, (name, field) in enumerate(fields):
for n, f in state.models[app_label, self.model_name_lower].fields if name == self.old_name:
] fields[index] = (self.new_name, field)
# Delay rendering of relationships if it's not a relational field.
delay = not field.is_relation
break
else:
raise FieldDoesNotExist(
"%s.%s has no field named '%s'" % (app_label, self.model_name, self.old_name)
)
# Fix index/unique_together to refer to the new field # Fix index/unique_together to refer to the new field
options = state.models[app_label, self.model_name_lower].options options = model_state.options
for option in ('index_together', 'unique_together'): for option in ('index_together', 'unique_together'):
if option in options: if option in options:
options[option] = [ options[option] = [
[self.new_name if n == self.old_name else n for n in together] [self.new_name if n == self.old_name else n for n in together]
for together in options[option] for together in options[option]
] ]
for n, f in state.models[app_label, self.model_name_lower].fields:
if n == self.new_name:
field = f
break
# Delay rendering of relationships if it's not a relational field
delay = not field.is_relation
state.reload_model(app_label, self.model_name_lower, delay=delay) state.reload_model(app_label, self.model_name_lower, delay=delay)
def database_forwards(self, app_label, schema_editor, from_state, to_state): def database_forwards(self, app_label, schema_editor, from_state, to_state):

View File

@ -54,3 +54,6 @@ Bugfixes
* Prevented a primary key alteration from adding a foreign key constraint if * Prevented a primary key alteration from adding a foreign key constraint if
``db_constraint=False`` (:ticket:`28298`). ``db_constraint=False`` (:ticket:`28298`).
* Fixed ``UnboundLocalError`` crash in ``RenameField`` with nonexistent field
(:ticket:`28350`).

View File

@ -1,5 +1,6 @@
import unittest import unittest
from django.core.exceptions import FieldDoesNotExist
from django.db import connection, migrations, models, transaction from django.db import connection, migrations, models, transaction
from django.db.migrations.migration import Migration from django.db.migrations.migration import Migration
from django.db.migrations.operations import CreateModel from django.db.migrations.operations import CreateModel
@ -1368,6 +1369,12 @@ class OperationTests(OperationTestBase):
self.assertEqual(definition[1], []) self.assertEqual(definition[1], [])
self.assertEqual(definition[2], {'model_name': "Pony", 'old_name': "pink", 'new_name': "blue"}) self.assertEqual(definition[2], {'model_name': "Pony", 'old_name': "pink", 'new_name': "blue"})
def test_rename_missing_field(self):
state = ProjectState()
state.add_model(ModelState('app', 'model', []))
with self.assertRaisesMessage(FieldDoesNotExist, "app.model has no field named 'field'"):
migrations.RenameField('model', 'field', 'new_field').state_forwards('app', state)
def test_alter_unique_together(self): def test_alter_unique_together(self):
""" """
Tests the AlterUniqueTogether operation. Tests the AlterUniqueTogether operation.