From 5e04e84d67da8163f365e9f5fcd169e2630e2873 Mon Sep 17 00:00:00 2001 From: Yuekui Li Date: Wed, 19 May 2021 16:30:15 -0700 Subject: [PATCH] Fixed #32503 -- Fixed altering BLOB/TEXT field to non-nullable with default on MySQL 8.0.13+. MySQL 8.0.13+ supports defaults for BLOB/TEXT but not in the ALTER COLUMN statement. Regression in 6b16c91157512587017e9178d066ed1a683e7795. Thanks Matt Westcott for the report. --- django/db/backends/base/schema.py | 9 ++++++++- django/db/backends/mysql/schema.py | 7 +++++++ tests/schema/models.py | 1 + tests/schema/tests.py | 9 +++++++++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py index 4998a3fe2e..ad2f5a7da1 100644 --- a/django/db/backends/base/schema.py +++ b/django/db/backends/base/schema.py @@ -266,6 +266,13 @@ class BaseDatabaseSchemaEditor: """ return False + def skip_default_on_alter(self, field): + """ + Some backends don't accept default values for certain columns types + (i.e. MySQL longtext and longblob) in the ALTER COLUMN statement. + """ + return False + def prepare_default(self, value): """ Only used for backends which have requires_literal_defaults feature @@ -721,7 +728,7 @@ class BaseDatabaseSchemaEditor: old_default = self.effective_default(old_field) new_default = self.effective_default(new_field) if ( - not self.skip_default(new_field) and + not self.skip_default_on_alter(new_field) and old_default != new_default and new_default is not None ): diff --git a/django/db/backends/mysql/schema.py b/django/db/backends/mysql/schema.py index 9bbcffc899..39450dd50d 100644 --- a/django/db/backends/mysql/schema.py +++ b/django/db/backends/mysql/schema.py @@ -68,6 +68,13 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): return self._is_limited_data_type(field) return False + def skip_default_on_alter(self, field): + if self._is_limited_data_type(field) and not self.connection.mysql_is_mariadb: + # MySQL doesn't support defaults for BLOB and TEXT in the + # ALTER COLUMN statement. + return True + return False + @property def _supports_limited_data_type_defaults(self): # MariaDB >= 10.2.1 and MySQL >= 8.0.13 supports defaults for BLOB diff --git a/tests/schema/models.py b/tests/schema/models.py index 6d4465807a..75e4de0874 100644 --- a/tests/schema/models.py +++ b/tests/schema/models.py @@ -158,6 +158,7 @@ class IntegerPK(models.Model): class Note(models.Model): info = models.TextField() + address = models.TextField(null=True) class Meta: apps = new_apps diff --git a/tests/schema/tests.py b/tests/schema/tests.py index 8587d2b15d..c5f5bd2e85 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -778,6 +778,15 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.alter_field(Note, old_field, new_field, strict=True) + def test_alter_text_field_to_not_null_with_default_value(self): + with connection.schema_editor() as editor: + editor.create_model(Note) + old_field = Note._meta.get_field('address') + new_field = TextField(blank=True, default='', null=False) + new_field.set_attributes_from_name('address') + with connection.schema_editor() as editor: + editor.alter_field(Note, old_field, new_field, strict=True) + @skipUnlessDBFeature('can_defer_constraint_checks', 'can_rollback_ddl') def test_alter_fk_checks_deferred_constraints(self): """