From fa0433d05f213afe4c67055006320f7aba4c8108 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Thu, 10 Jun 2021 09:39:08 +0200 Subject: [PATCH] Fixed #32832 -- Fixed adding BLOB/TEXT nullable field with default on MySQL 8.0.13+. Regression in d4ac23bee1c84d8e4610350202ac068fc90f38c0. Thanks Omkar Deshpande for the report. --- django/db/backends/base/schema.py | 11 +++++++++-- docs/releases/3.2.5.txt | 4 ++++ tests/schema/tests.py | 27 +++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py index c409464eca..8050441414 100644 --- a/django/db/backends/base/schema.py +++ b/django/db/backends/base/schema.py @@ -225,7 +225,14 @@ class BaseDatabaseSchemaEditor: # Work out nullability null = field.null # If we were told to include a default value, do so - include_default = include_default and not self.skip_default(field) + include_default = ( + include_default and + not self.skip_default(field) and + # Don't include a default value if it's a nullable field and the + # default cannot be dropped in the ALTER COLUMN statement (e.g. + # MySQL longtext and longblob). + not (null and self.skip_default_on_alter(field)) + ) if include_default: default_value = self.effective_default(field) column_default = ' DEFAULT ' + self._column_default_sql(field) @@ -515,7 +522,7 @@ class BaseDatabaseSchemaEditor: self.execute(sql, params) # Drop the default if we need to # (Django usually does not use in-database defaults) - if not self.skip_default(field) and self.effective_default(field) is not None: + if not self.skip_default_on_alter(field) and self.effective_default(field) is not None: changes_sql, params = self._alter_column_default_sql(model, None, field, drop=True) sql = self.sql_alter_column % { "table": self.quote_name(model._meta.db_table), diff --git a/docs/releases/3.2.5.txt b/docs/releases/3.2.5.txt index f426d437ee..05d824fc25 100644 --- a/docs/releases/3.2.5.txt +++ b/docs/releases/3.2.5.txt @@ -16,3 +16,7 @@ Bugfixes * Fixed a bug in Django 3.2 that caused a migration crash on MySQL 8.0.13+ when altering ``BinaryField``, ``JSONField``, or ``TextField`` to non-nullable (:ticket:`32503`). + +* Fixed a regression in Django 3.2 that caused a migration crash on MySQL + 8.0.13+ when adding nullable ``BinaryField``, ``JSONField``, or ``TextField`` + with a default value (:ticket:`32832`). diff --git a/tests/schema/tests.py b/tests/schema/tests.py index eb9be178db..0e88bfdbd1 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -3387,6 +3387,33 @@ class SchemaTests(TransactionTestCase): if connection.features.can_introspect_default: self.assertIn(field.default, ['NULL', None]) + def test_add_textfield_default_nullable(self): + with connection.schema_editor() as editor: + editor.create_model(Author) + # Add new nullable TextField with a default. + new_field = TextField(blank=True, null=True, default='text') + new_field.set_attributes_from_name('description') + with connection.schema_editor() as editor: + editor.add_field(Author, new_field) + Author.objects.create(name='Anonymous1') + with connection.cursor() as cursor: + cursor.execute('SELECT description FROM schema_author;') + item = cursor.fetchall()[0] + self.assertIsNone(item[0]) + field = next( + f + for f in connection.introspection.get_table_description( + cursor, + 'schema_author', + ) + if f.name == 'description' + ) + # Field is still nullable. + self.assertTrue(field.null_ok) + # The database default is no longer set. + if connection.features.can_introspect_default: + self.assertIn(field.default, ['NULL', None]) + def test_alter_field_default_dropped(self): # Create the table with connection.schema_editor() as editor: