From 1eb3f500a44b08a05868cd8bd85501dd6c7ee6c7 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Tue, 31 Aug 2021 13:43:10 +0200 Subject: [PATCH] Fixed #33057 -- Fixed recreation of foreign key constraints in m2m tables when altering type of referenced primary key on Oracle. --- django/db/backends/oracle/schema.py | 15 ++++++++++++++- tests/migrations/test_operations.py | 22 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/django/db/backends/oracle/schema.py b/django/db/backends/oracle/schema.py index cf875fc5e3e..0d1c7f5d4be 100644 --- a/django/db/backends/oracle/schema.py +++ b/django/db/backends/oracle/schema.py @@ -3,7 +3,9 @@ import datetime import re from django.db import DatabaseError -from django.db.backends.base.schema import BaseDatabaseSchemaEditor +from django.db.backends.base.schema import ( + BaseDatabaseSchemaEditor, _related_non_m2m_objects, +) class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): @@ -124,6 +126,17 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): self.remove_field(model, old_field) # Rename and possibly make the new field NOT NULL super().alter_field(model, new_temp_field, new_field) + # Recreate foreign key (if necessary) because the old field is not + # passed to the alter_field() and data types of new_temp_field and + # new_field always match. + new_type = new_field.db_type(self.connection) + if ( + (old_field.primary_key and new_field.primary_key) or + (old_field.unique and new_field.unique) + ) and old_type != new_type: + for _, rel in _related_non_m2m_objects(new_temp_field, new_field): + if rel.field.db_constraint: + self.execute(self._create_fk_sql(rel.related_model, rel.field, '_fk')) def _alter_column_type_sql(self, model, old_field, new_field, new_type): auto_field_types = {'AutoField', 'BigAutoField', 'SmallAutoField'} diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py index 6d016a2a25b..5ae75a477b4 100644 --- a/tests/migrations/test_operations.py +++ b/tests/migrations/test_operations.py @@ -1500,10 +1500,32 @@ class OperationTests(OperationTestBase): with connection.schema_editor() as editor: operation.database_forwards("test_alflpkfk", editor, project_state, new_state) assertIdTypeEqualsFkType() + if connection.features.supports_foreign_keys: + self.assertFKExists( + 'test_alflpkfk_pony_stables', + ['pony_id'], + ('test_alflpkfk_pony', 'id'), + ) + self.assertFKExists( + 'test_alflpkfk_stable_ponies', + ['pony_id'], + ('test_alflpkfk_pony', 'id'), + ) # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_alflpkfk", editor, new_state, project_state) assertIdTypeEqualsFkType() + if connection.features.supports_foreign_keys: + self.assertFKExists( + 'test_alflpkfk_pony_stables', + ['pony_id'], + ('test_alflpkfk_pony', 'id'), + ) + self.assertFKExists( + 'test_alflpkfk_stable_ponies', + ['pony_id'], + ('test_alflpkfk_pony', 'id'), + ) def test_alter_field_pk_mti_fk(self): app_label = 'test_alflpkmtifk'