summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndriy Sokolovskiy <me@asokolovskiy.com>2015-05-27 01:18:21 +0300
committerTim Graham <timograham@gmail.com>2015-05-28 10:26:27 -0400
commit927d90ee1ee4aa630a5a879b5fd75aa03a3341f7 (patch)
tree6884a0950ada84d3c69707d10365032202b7403f
parent8bb369ef6345074216471c41bd575d540992c76e (diff)
downloaddjango-927d90ee1ee4aa630a5a879b5fd75aa03a3341f7.tar.gz
[1.7.x] Fixed #24817 -- Prevented loss of null info in MySQL field renaming.
Backport of 80ad5472ce4b6ba6e94227422d0727371e97cdf0 from master
-rw-r--r--django/db/backends/mysql/schema.py17
-rw-r--r--django/db/backends/schema.py15
-rw-r--r--docs/releases/1.7.9.txt10
-rw-r--r--docs/releases/index.txt1
-rw-r--r--tests/schema/models.py8
-rw-r--r--tests/schema/tests.py48
6 files changed, 81 insertions, 18 deletions
diff --git a/django/db/backends/mysql/schema.py b/django/db/backends/mysql/schema.py
index 37c130e908..15286a67a1 100644
--- a/django/db/backends/mysql/schema.py
+++ b/django/db/backends/mysql/schema.py
@@ -49,10 +49,21 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
'column': self.quote_name(field.column),
}, [effective_default])
- def _alter_column_type_sql(self, table, old_field, new_field, new_type):
- # Keep null property of old field, if it has changed, it will be handled separately
- if old_field.null:
+ def _set_field_new_type_null_status(self, field, new_type):
+ """
+ Keep the null property of the old field. If it has changed, it will be
+ handled separately.
+ """
+ if field.null:
new_type += " NULL"
else:
new_type += " NOT NULL"
+ return new_type
+
+ def _alter_column_type_sql(self, table, old_field, new_field, new_type):
+ new_type = self._set_field_new_type_null_status(old_field, new_type)
return super(DatabaseSchemaEditor, self)._alter_column_type_sql(table, old_field, new_field, new_type)
+
+ def _rename_field_sql(self, table, old_field, new_field, new_type):
+ new_type = self._set_field_new_type_null_status(old_field, new_type)
+ return super(DatabaseSchemaEditor, self)._rename_field_sql(table, old_field, new_field, new_type)
diff --git a/django/db/backends/schema.py b/django/db/backends/schema.py
index 6febdb134d..0d9ebf7d89 100644
--- a/django/db/backends/schema.py
+++ b/django/db/backends/schema.py
@@ -527,12 +527,7 @@ class BaseDatabaseSchemaEditor(object):
self.execute(self._delete_constraint_sql(self.sql_delete_check, model, constraint_name))
# Have they renamed the column?
if old_field.column != new_field.column:
- self.execute(self.sql_rename_column % {
- "table": self.quote_name(model._meta.db_table),
- "old_column": self.quote_name(old_field.column),
- "new_column": self.quote_name(new_field.column),
- "type": new_type,
- })
+ self.execute(self._rename_field_sql(model._meta.db_table, old_field, new_field, new_type))
# Next, start accumulating actions to do
actions = []
null_actions = []
@@ -841,6 +836,14 @@ class BaseDatabaseSchemaEditor(object):
output.append(self._create_index_sql(model, fields, suffix="_idx"))
return output
+ def _rename_field_sql(self, table, old_field, new_field, new_type):
+ return self.sql_rename_column % {
+ "table": self.quote_name(table),
+ "old_column": self.quote_name(old_field.column),
+ "new_column": self.quote_name(new_field.column),
+ "type": new_type,
+ }
+
def _create_fk_sql(self, model, field, suffix):
from_table = model._meta.db_table
from_column = field.column
diff --git a/docs/releases/1.7.9.txt b/docs/releases/1.7.9.txt
new file mode 100644
index 0000000000..bca875803c
--- /dev/null
+++ b/docs/releases/1.7.9.txt
@@ -0,0 +1,10 @@
+==========================
+Django 1.7.9 release notes
+==========================
+
+*Under development*
+
+Django 1.7.9 fixes several bugs in 1.7.8.
+
+* Prevented the loss of ``null``/``not null`` column properties during field
+ renaming of MySQL databases (:ticket:`24817`).
diff --git a/docs/releases/index.txt b/docs/releases/index.txt
index 15473f56c6..a574ef67a2 100644
--- a/docs/releases/index.txt
+++ b/docs/releases/index.txt
@@ -25,6 +25,7 @@ versions of the documentation contain the release notes for any later releases.
.. toctree::
:maxdepth: 1
+ 1.7.9
1.7.8
1.7.7
1.7.6
diff --git a/tests/schema/models.py b/tests/schema/models.py
index b9331750c4..507acb1a23 100644
--- a/tests/schema/models.py
+++ b/tests/schema/models.py
@@ -122,6 +122,14 @@ class BookWithSlug(models.Model):
db_table = "schema_book"
+class NoteRename(models.Model):
+ detail_info = models.TextField()
+
+ class Meta:
+ apps = new_apps
+ db_table = "schema_note"
+
+
class Tag(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField(unique=True)
diff --git a/tests/schema/tests.py b/tests/schema/tests.py
index 7b4f751852..09a0b7646f 100644
--- a/tests/schema/tests.py
+++ b/tests/schema/tests.py
@@ -1,17 +1,27 @@
import datetime
import unittest
-from django.test import TransactionTestCase, skipIfDBFeature
-from django.db import connection, DatabaseError, IntegrityError, OperationalError
-from django.db.models.fields import (BigIntegerField, BinaryField, BooleanField, CharField,
- IntegerField, PositiveIntegerField, SlugField, TextField)
-from django.db.models.fields.related import ForeignKey, ManyToManyField, OneToOneField
+from django.db import (
+ DatabaseError, IntegrityError, OperationalError, connection,
+)
+from django.db.models.fields import (
+ BigIntegerField, BinaryField, BooleanField, CharField, IntegerField,
+ PositiveIntegerField, SlugField, TextField,
+)
+from django.db.models.fields.related import (
+ ForeignKey, ManyToManyField, OneToOneField,
+)
from django.db.transaction import atomic
+from django.test import TransactionTestCase, skipIfDBFeature
+
from .fields import CustomManyToManyField, InheritedManyToManyField
-from .models import (Author, AuthorWithDefaultHeight, AuthorWithM2M, Book, BookWithLongName,
- BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename,
- UniqueTest, Thing, TagThrough, BookWithM2MThrough, AuthorTag, AuthorWithM2MThrough,
- AuthorWithEvenLongerName, BookWeak, Note, BookWithO2O, BookWithoutFK)
+from .models import (
+ Author, AuthorTag, AuthorWithDefaultHeight, AuthorWithEvenLongerName,
+ AuthorWithM2M, AuthorWithM2MThrough, Book, BookWeak, BookWithLongName,
+ BookWithM2M, BookWithM2MThrough, BookWithO2O, BookWithoutFK, BookWithSlug,
+ Note, NoteRename, Tag, TagIndexed, TagM2MTest, TagThrough, TagUniqueRename,
+ Thing, UniqueTest,
+)
class SchemaTests(TransactionTestCase):
@@ -754,6 +764,26 @@ class SchemaTests(TransactionTestCase):
self.assertEqual(columns['display_name'][0], "CharField")
self.assertNotIn("name", columns)
+ @skipIfDBFeature('interprets_empty_strings_as_nulls')
+ def test_rename_keep_null_status(self):
+ """
+ Renaming a field shouldn't affect the not null status.
+ """
+ with connection.schema_editor() as editor:
+ editor.create_model(Note)
+ with self.assertRaises(IntegrityError):
+ Note.objects.create(info=None)
+ old_field = Note._meta.get_field("info")
+ new_field = TextField()
+ new_field.set_attributes_from_name("detail_info")
+ with connection.schema_editor() as editor:
+ editor.alter_field(Note, old_field, new_field, strict=True)
+ columns = self.column_classes(Note)
+ self.assertEqual(columns['detail_info'][0], "TextField")
+ self.assertNotIn("info", columns)
+ with self.assertRaises(IntegrityError):
+ NoteRename.objects.create(detail_info=None)
+
def test_m2m_create(self):
"""
Tests M2M fields on models during creation