summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2022-01-27 18:51:39 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2022-01-27 18:52:35 +0100
commit7c2d4d943b2905a8cfbb2d2f6c6fa15983b23ff1 (patch)
treeb3cff2afea69926c8b611e40987ff8c714b2ddb9
parentf4de87038ef3adfe8c67ede6c3c5519451abe6cc (diff)
[4.0.x] Fixed #33462 -- Fixed migration crash when altering type of primary key with MTI and foreign key.
This prevents duplicated operations when altering type of primary key with MTI and foreign key. Previously, a foreign key to the base model was added twice, once directly and once by the inheritance model. Thanks bcail for the report. Regression in 325d7710ce9f6155bb55610ad6b4580d31263557. Backport of e972620ada4f9ed7bc57f28e133e85c85b0a7b20 from main
-rw-r--r--django/db/backends/base/schema.py4
-rw-r--r--docs/releases/4.0.2.txt4
-rw-r--r--tests/migrations/test_operations.py67
3 files changed, 74 insertions, 1 deletions
diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py
index 896ea3d517..da3ec1879d 100644
--- a/django/db/backends/base/schema.py
+++ b/django/db/backends/base/schema.py
@@ -30,7 +30,9 @@ def _is_relevant_relation(relation, altered_field):
def _all_related_fields(model):
- return model._meta._get_fields(forward=False, reverse=True, include_hidden=True)
+ return model._meta._get_fields(
+ forward=False, reverse=True, include_hidden=True, include_parents=False,
+ )
def _related_non_m2m_objects(old_field, new_field):
diff --git a/docs/releases/4.0.2.txt b/docs/releases/4.0.2.txt
index 8227d98622..af0f728c54 100644
--- a/docs/releases/4.0.2.txt
+++ b/docs/releases/4.0.2.txt
@@ -28,3 +28,7 @@ Bugfixes
* Fixed a regression in Django 4.0 that caused incorrect
:attr:`.ModelAdmin.radio_fields` layout in the admin (:ticket:`33407`).
+
+* Fixed a duplicate operation regression in Django 4.0 that caused a migration
+ crash when altering a primary key type for a concrete parent model referenced
+ by a foreign key (:ticket:`33462`).
diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py
index 11961a1f40..5e2edc5297 100644
--- a/tests/migrations/test_operations.py
+++ b/tests/migrations/test_operations.py
@@ -1600,6 +1600,73 @@ class OperationTests(OperationTestBase):
(f'{app_label}_shetlandpony', 'pony_ptr_id'),
)
+ def test_alter_field_pk_mti_and_fk_to_base(self):
+ app_label = 'test_alflpkmtiftb'
+ project_state = self.set_up_test_model(
+ app_label, mti_model=True, related_model=True,
+ )
+ operation = migrations.AlterField(
+ 'Pony',
+ 'id',
+ models.BigAutoField(primary_key=True),
+ )
+ new_state = project_state.clone()
+ operation.state_forwards(app_label, new_state)
+ self.assertIsInstance(
+ new_state.models[app_label, 'pony'].fields['id'],
+ models.BigAutoField,
+ )
+
+ def _get_column_id_type(cursor, table, column):
+ return [
+ c.type_code
+ for c in connection.introspection.get_table_description(
+ cursor,
+ f'{app_label}_{table}',
+ )
+ if c.name == column
+ ][0]
+
+ def assertIdTypeEqualsMTIFkType():
+ with connection.cursor() as cursor:
+ parent_id_type = _get_column_id_type(cursor, 'pony', 'id')
+ fk_id_type = _get_column_id_type(cursor, 'rider', 'pony_id')
+ child_id_type = _get_column_id_type(cursor, 'shetlandpony', 'pony_ptr_id')
+ self.assertEqual(parent_id_type, child_id_type)
+ self.assertEqual(parent_id_type, fk_id_type)
+
+ assertIdTypeEqualsMTIFkType()
+ # Alter primary key.
+ with connection.schema_editor() as editor:
+ operation.database_forwards(app_label, editor, project_state, new_state)
+ assertIdTypeEqualsMTIFkType()
+ if connection.features.supports_foreign_keys:
+ self.assertFKExists(
+ f'{app_label}_shetlandpony',
+ ['pony_ptr_id'],
+ (f'{app_label}_pony', 'id'),
+ )
+ self.assertFKExists(
+ f'{app_label}_rider',
+ ['pony_id'],
+ (f'{app_label}_pony', 'id'),
+ )
+ # Reversal.
+ with connection.schema_editor() as editor:
+ operation.database_backwards(app_label, editor, new_state, project_state)
+ assertIdTypeEqualsMTIFkType()
+ if connection.features.supports_foreign_keys:
+ self.assertFKExists(
+ f'{app_label}_shetlandpony',
+ ['pony_ptr_id'],
+ (f'{app_label}_pony', 'id'),
+ )
+ self.assertFKExists(
+ f'{app_label}_rider',
+ ['pony_id'],
+ (f'{app_label}_pony', 'id'),
+ )
+
@skipUnlessDBFeature('supports_foreign_keys')
def test_alter_field_reloads_state_on_fk_with_to_field_target_type_change(self):
app_label = 'test_alflrsfkwtflttc'