[1.7.x] Fixed #24817 -- Prevented loss of null info in MySQL field renaming.
Backport of 80ad5472ce4b6ba6e94227422d0727371e97cdf0 from master
This commit is contained in:
parent
8bb369ef63
commit
927d90ee1e
@ -49,10 +49,21 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|||||||
'column': self.quote_name(field.column),
|
'column': self.quote_name(field.column),
|
||||||
}, [effective_default])
|
}, [effective_default])
|
||||||
|
|
||||||
def _alter_column_type_sql(self, table, old_field, new_field, new_type):
|
def _set_field_new_type_null_status(self, field, new_type):
|
||||||
# Keep null property of old field, if it has changed, it will be handled separately
|
"""
|
||||||
if old_field.null:
|
Keep the null property of the old field. If it has changed, it will be
|
||||||
|
handled separately.
|
||||||
|
"""
|
||||||
|
if field.null:
|
||||||
new_type += " NULL"
|
new_type += " NULL"
|
||||||
else:
|
else:
|
||||||
new_type += " NOT NULL"
|
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)
|
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)
|
||||||
|
@ -527,12 +527,7 @@ class BaseDatabaseSchemaEditor(object):
|
|||||||
self.execute(self._delete_constraint_sql(self.sql_delete_check, model, constraint_name))
|
self.execute(self._delete_constraint_sql(self.sql_delete_check, model, constraint_name))
|
||||||
# Have they renamed the column?
|
# Have they renamed the column?
|
||||||
if old_field.column != new_field.column:
|
if old_field.column != new_field.column:
|
||||||
self.execute(self.sql_rename_column % {
|
self.execute(self._rename_field_sql(model._meta.db_table, old_field, new_field, new_type))
|
||||||
"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,
|
|
||||||
})
|
|
||||||
# Next, start accumulating actions to do
|
# Next, start accumulating actions to do
|
||||||
actions = []
|
actions = []
|
||||||
null_actions = []
|
null_actions = []
|
||||||
@ -841,6 +836,14 @@ class BaseDatabaseSchemaEditor(object):
|
|||||||
output.append(self._create_index_sql(model, fields, suffix="_idx"))
|
output.append(self._create_index_sql(model, fields, suffix="_idx"))
|
||||||
return output
|
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):
|
def _create_fk_sql(self, model, field, suffix):
|
||||||
from_table = model._meta.db_table
|
from_table = model._meta.db_table
|
||||||
from_column = field.column
|
from_column = field.column
|
||||||
|
10
docs/releases/1.7.9.txt
Normal file
10
docs/releases/1.7.9.txt
Normal file
@ -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`).
|
@ -25,6 +25,7 @@ versions of the documentation contain the release notes for any later releases.
|
|||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
|
1.7.9
|
||||||
1.7.8
|
1.7.8
|
||||||
1.7.7
|
1.7.7
|
||||||
1.7.6
|
1.7.6
|
||||||
|
@ -122,6 +122,14 @@ class BookWithSlug(models.Model):
|
|||||||
db_table = "schema_book"
|
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):
|
class Tag(models.Model):
|
||||||
title = models.CharField(max_length=255)
|
title = models.CharField(max_length=255)
|
||||||
slug = models.SlugField(unique=True)
|
slug = models.SlugField(unique=True)
|
||||||
|
@ -1,17 +1,27 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from django.test import TransactionTestCase, skipIfDBFeature
|
from django.db import (
|
||||||
from django.db import connection, DatabaseError, IntegrityError, OperationalError
|
DatabaseError, IntegrityError, OperationalError, connection,
|
||||||
from django.db.models.fields import (BigIntegerField, BinaryField, BooleanField, CharField,
|
)
|
||||||
IntegerField, PositiveIntegerField, SlugField, TextField)
|
from django.db.models.fields import (
|
||||||
from django.db.models.fields.related import ForeignKey, ManyToManyField, OneToOneField
|
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.db.transaction import atomic
|
||||||
|
from django.test import TransactionTestCase, skipIfDBFeature
|
||||||
|
|
||||||
from .fields import CustomManyToManyField, InheritedManyToManyField
|
from .fields import CustomManyToManyField, InheritedManyToManyField
|
||||||
from .models import (Author, AuthorWithDefaultHeight, AuthorWithM2M, Book, BookWithLongName,
|
from .models import (
|
||||||
BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename,
|
Author, AuthorTag, AuthorWithDefaultHeight, AuthorWithEvenLongerName,
|
||||||
UniqueTest, Thing, TagThrough, BookWithM2MThrough, AuthorTag, AuthorWithM2MThrough,
|
AuthorWithM2M, AuthorWithM2MThrough, Book, BookWeak, BookWithLongName,
|
||||||
AuthorWithEvenLongerName, BookWeak, Note, BookWithO2O, BookWithoutFK)
|
BookWithM2M, BookWithM2MThrough, BookWithO2O, BookWithoutFK, BookWithSlug,
|
||||||
|
Note, NoteRename, Tag, TagIndexed, TagM2MTest, TagThrough, TagUniqueRename,
|
||||||
|
Thing, UniqueTest,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SchemaTests(TransactionTestCase):
|
class SchemaTests(TransactionTestCase):
|
||||||
@ -754,6 +764,26 @@ class SchemaTests(TransactionTestCase):
|
|||||||
self.assertEqual(columns['display_name'][0], "CharField")
|
self.assertEqual(columns['display_name'][0], "CharField")
|
||||||
self.assertNotIn("name", columns)
|
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):
|
def test_m2m_create(self):
|
||||||
"""
|
"""
|
||||||
Tests M2M fields on models during creation
|
Tests M2M fields on models during creation
|
||||||
|
Loading…
x
Reference in New Issue
Block a user