Browse Source

Fixed #35487 -- Removed CASCADE from RemoveField() on PostgreSQL.

Co-authored-by: Mariusz Felisiak <felisiak.mariusz@gmail.com>
Co-authored-by: Adam Johnson <me@adamj.eu>
Co-authored-by: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
petr.prikryl 9 months ago
parent
commit
2a5aca38bb

+ 1 - 1
django/db/backends/base/schema.py

@@ -95,7 +95,7 @@ class BaseDatabaseSchemaEditor:
     sql_alter_column_default = "ALTER COLUMN %(column)s SET DEFAULT %(default)s"
     sql_alter_column_no_default = "ALTER COLUMN %(column)s DROP DEFAULT"
     sql_alter_column_no_default_null = sql_alter_column_no_default
-    sql_delete_column = "ALTER TABLE %(table)s DROP COLUMN %(column)s CASCADE"
+    sql_delete_column = "ALTER TABLE %(table)s DROP COLUMN %(column)s"
     sql_rename_column = (
         "ALTER TABLE %(table)s RENAME COLUMN %(old_column)s TO %(new_column)s"
     )

+ 0 - 3
django/db/backends/mysql/schema.py

@@ -11,9 +11,6 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
     sql_alter_column_type = "MODIFY %(column)s %(type)s%(collation)s%(comment)s"
     sql_alter_column_no_default_null = "ALTER COLUMN %(column)s SET DEFAULT NULL"
 
-    # No 'CASCADE' which works as a no-op in MySQL but is undocumented
-    sql_delete_column = "ALTER TABLE %(table)s DROP COLUMN %(column)s"
-
     sql_delete_unique = "ALTER TABLE %(table)s DROP INDEX %(name)s"
     sql_create_column_inline_fk = (
         ", ADD CONSTRAINT %(name)s FOREIGN KEY (%(column)s) "

+ 0 - 1
django/db/backends/oracle/schema.py

@@ -19,7 +19,6 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
     sql_alter_column_no_default = "MODIFY %(column)s DEFAULT NULL"
     sql_alter_column_no_default_null = sql_alter_column_no_default
 
-    sql_delete_column = "ALTER TABLE %(table)s DROP COLUMN %(column)s"
     sql_create_column_inline_fk = (
         "CONSTRAINT %(name)s REFERENCES %(to_table)s(%(to_column)s)%(deferrable)s"
     )

+ 0 - 1
django/db/backends/sqlite3/schema.py

@@ -16,7 +16,6 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
         "REFERENCES %(to_table)s (%(to_column)s) DEFERRABLE INITIALLY DEFERRED"
     )
     sql_create_column_inline_fk = sql_create_inline_fk
-    sql_delete_column = "ALTER TABLE %(table)s DROP COLUMN %(column)s"
     sql_create_unique = "CREATE UNIQUE INDEX %(name)s ON %(table)s (%(columns)s)"
     sql_delete_unique = "DROP INDEX %(name)s"
     sql_alter_table_comment = None

+ 5 - 8
docs/ref/migration-operations.txt

@@ -195,15 +195,12 @@ if the field is nullable or if it has a default value that can be used to
 populate the recreated column. If the field is not nullable and does not have a
 default value, the operation is irreversible.
 
-.. admonition:: PostgreSQL
-
-    ``RemoveField`` will also delete any additional database objects that are
-    related to the removed field (like views, for example). This is because the
-    resulting ``DROP COLUMN`` statement will include the ``CASCADE`` clause to
-    ensure `dependent objects outside the table are also dropped`_.
-
-.. _dependent objects outside the table are also dropped: https://www.postgresql.org/docs/current/sql-altertable.html#SQL-ALTERTABLE-PARMS-CASCADE
+.. versionchanged:: 6.0
 
+    :class:`~django.db.backends.base.schema.BaseDatabaseSchemaEditor` and
+    PostgreSQL backends no longer use ``CASCADE`` to delete dependent related
+    database objects, such as views. Any dependent objects that are not managed
+    by Django may need to be removed manually before running ``RemoveField``.
 
 ``AlterField``
 --------------

+ 3 - 0
docs/releases/6.0.txt

@@ -254,6 +254,9 @@ backends.
 * ``BaseDatabaseCreation.create_test_db(serialize)`` is deprecated. Use
   ``serialize_db_to_string()`` instead.
 
+* :class:`~django.db.backends.base.schema.BaseDatabaseSchemaEditor` and
+  PostgreSQL backends no longer use ``CASCADE`` when dropping a column.
+
 Dropped support for MariaDB 10.5
 --------------------------------
 

+ 6 - 1
tests/migrations/test_operations.py

@@ -2055,8 +2055,13 @@ class OperationTests(OperationTestBase):
         self.assertEqual(len(new_state.models["test_rmfl", "pony"].fields), 4)
         # Test the database alteration
         self.assertColumnExists("test_rmfl_pony", "pink")
-        with connection.schema_editor() as editor:
+        with (
+            connection.schema_editor() as editor,
+            CaptureQueriesContext(connection) as ctx,
+        ):
             operation.database_forwards("test_rmfl", editor, project_state, new_state)
+        self.assertGreater(len(ctx.captured_queries), 0)
+        self.assertNotIn("CASCADE", ctx.captured_queries[-1]["sql"])
         self.assertColumnNotExists("test_rmfl_pony", "pink")
         # And test reversal
         with connection.schema_editor() as editor: