From 2f73e5406d54cb8945e187eff302a3a3373350be Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Fri, 10 Dec 2021 11:12:04 +0100 Subject: [PATCH] Refs #32502 -- Avoided table rebuild when adding fields with no default on SQLite. --- django/db/backends/sqlite3/schema.py | 17 +++++++++-------- tests/schema/tests.py | 9 +++++++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/django/db/backends/sqlite3/schema.py b/django/db/backends/sqlite3/schema.py index fd3d3c2a07..6752a8e3c0 100644 --- a/django/db/backends/sqlite3/schema.py +++ b/django/db/backends/sqlite3/schema.py @@ -15,6 +15,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): sql_delete_table = "DROP TABLE %(table)s" sql_create_fk = None sql_create_inline_fk = "REFERENCES %(to_table)s (%(to_column)s) DEFERRABLE INITIALLY DEFERRED" + sql_create_column_inline_fk = sql_create_inline_fk sql_create_unique = "CREATE UNIQUE INDEX %(name)s ON %(table)s (%(columns)s)" sql_delete_unique = "DROP INDEX %(name)s" @@ -322,14 +323,14 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): self.deferred_sql.remove(sql) def add_field(self, model, field): - """ - Create a field on a model. Usually involves adding a column, but may - involve adding a table instead (for M2M fields). - """ - # Special-case implicit M2M tables - if field.many_to_many and field.remote_field.through._meta.auto_created: - return self.create_model(field.remote_field.through) - self._remake_table(model, create_field=field) + """Create a field on a model.""" + # Fields with default values cannot by handled by ALTER TABLE ADD + # COLUMN statement because DROP DEFAULT is not supported in + # ALTER TABLE. + if not field.null or self.effective_default(field) is not None: + self._remake_table(model, create_field=field) + else: + super().add_field(model, field) def remove_field(self, model, field): """ diff --git a/tests/schema/tests.py b/tests/schema/tests.py index e94659544a..72a73745ed 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -517,6 +517,15 @@ class SchemaTests(TransactionTestCase): 'column': editor.quote_name(new_field.name), } self.assertFalse(any(drop_default_sql in query['sql'] for query in ctx.captured_queries)) + # Table is not rebuilt. + self.assertIs(any( + 'CREATE TABLE' in query['sql'] + for query in ctx.captured_queries + ), False) + self.assertIs(any( + 'DROP TABLE' in query['sql'] + for query in ctx.captured_queries + ), False) columns = self.column_classes(Author) self.assertEqual(columns['age'][0], connection.features.introspected_field_types['IntegerField']) self.assertTrue(columns['age'][1][6])