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.utils.functional import cached_property
@ -261,25 +262,27 @@ class RenameField(FieldOperation):
)
def state_forwards(self, app_label, state):
model_state = state.models[app_label, self.model_name_lower]
# Rename the field
state.models[app_label, self.model_name_lower].fields = [
(self.new_name if n == self.old_name else n, f)
for n, f in state.models[app_label, self.model_name_lower].fields
]
fields = model_state.fields
for index, (name, field) in enumerate(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
options = state.models[app_label, self.model_name_lower].options
options = model_state.options
for option in ('index_together', 'unique_together'):
if option in options:
options[option] = [
[self.new_name if n == self.old_name else n for n in together]
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)
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
``db_constraint=False`` (:ticket:`28298`).
* Fixed ``UnboundLocalError`` crash in ``RenameField`` with nonexistent field
(:ticket:`28350`).

View File

@ -1,5 +1,6 @@
import unittest
from django.core.exceptions import FieldDoesNotExist
from django.db import connection, migrations, models, transaction
from django.db.migrations.migration import Migration
from django.db.migrations.operations import CreateModel
@ -1368,6 +1369,12 @@ class OperationTests(OperationTestBase):
self.assertEqual(definition[1], [])
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):
"""
Tests the AlterUniqueTogether operation.