[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 in325d7710ce
. Backport ofe972620ada
from main
This commit is contained in:
parent
f4de87038e
commit
7c2d4d943b
|
@ -30,7 +30,9 @@ def _is_relevant_relation(relation, altered_field):
|
||||||
|
|
||||||
|
|
||||||
def _all_related_fields(model):
|
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):
|
def _related_non_m2m_objects(old_field, new_field):
|
||||||
|
|
|
@ -28,3 +28,7 @@ Bugfixes
|
||||||
|
|
||||||
* Fixed a regression in Django 4.0 that caused incorrect
|
* Fixed a regression in Django 4.0 that caused incorrect
|
||||||
:attr:`.ModelAdmin.radio_fields` layout in the admin (:ticket:`33407`).
|
: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`).
|
||||||
|
|
|
@ -1600,6 +1600,73 @@ class OperationTests(OperationTestBase):
|
||||||
(f'{app_label}_shetlandpony', 'pony_ptr_id'),
|
(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')
|
@skipUnlessDBFeature('supports_foreign_keys')
|
||||||
def test_alter_field_reloads_state_on_fk_with_to_field_target_type_change(self):
|
def test_alter_field_reloads_state_on_fk_with_to_field_target_type_change(self):
|
||||||
app_label = 'test_alflrsfkwtflttc'
|
app_label = 'test_alflrsfkwtflttc'
|
||||||
|
|
Loading…
Reference in New Issue