Ticket #30152: testcase_and_rough_fix.patch

File testcase_and_rough_fix.patch, 6.9 KB (added by Matthijs Kooijman, 3 years ago)

Testcase showing the problem, plus a rough stab at a fix

  • django/db/backends/base/schema.py

    diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py
    index 113d1b7f..273c5bb1 100644
    a b def _related_non_m2m_objects(old_field, new_field): 
    3232    # Filter out m2m objects from reverse relations.
    3333    # Return (old_relation, new_relation) tuples.
    3434    return zip(
    35         (obj for obj in old_field.model._meta.related_objects if _is_relevant_relation(obj, old_field)),
    36         (obj for obj in new_field.model._meta.related_objects if _is_relevant_relation(obj, new_field))
     35        (obj for obj in old_field.model._meta._get_fields(forward=False, reverse=True, include_hidden=True) if _is_relevant_relation(obj, old_field)),
     36        (obj for obj in new_field.model._meta._get_fields(forward=False, reverse=True, include_hidden=True) if _is_relevant_relation(obj, new_field)),
    3737    )
    3838
    3939
    class BaseDatabaseSchemaEditor: 
    743743            self.execute(self._create_fk_sql(model, new_field, "_fk_%(to_table)s_%(to_column)s"))
    744744        # Rebuild FKs that pointed to us if we previously had to drop them
    745745        if drop_foreign_keys:
    746             for rel in new_field.model._meta.related_objects:
     746            for rel in new_field.model._meta._get_fields(forward=False, reverse=True, include_hidden=True):
    747747                if _is_relevant_relation(rel, new_field) and rel.field.db_constraint:
    748748                    self.execute(self._create_fk_sql(rel.related_model, rel.field, "_fk"))
    749749        # Does it have check constraints we need to add?
  • tests/migrations/test_operations.py

    diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py
    index d5c83965..2006052f 100644
    a b class OperationTests(OperationTestBase): 
    14581458            operation.database_backwards("test_alflpkfk", editor, new_state, project_state)
    14591459        assertIdTypeEqualsFkType()
    14601460
     1461    @skipUnlessDBFeature('supports_foreign_keys')
     1462    def test_alter_field_pk_m2m(self):
     1463        """
     1464        Tests the AlterField operation on primary keys changes any FKs pointing to it.
     1465        """
     1466        project_state = self.set_up_test_model("test_alflpkm2m", second_model=True)
     1467
     1468        project_state = self.apply_operations("test_alflpkm2m", project_state, operations=[
     1469            migrations.AddField(
     1470                "Pony", "stables",
     1471                models.ManyToManyField("Stable", related_name="ponies")
     1472            )
     1473        ])
     1474
     1475        # Test the state alteration
     1476        operation = migrations.AlterField("Pony", "id", models.FloatField(primary_key=True))
     1477        new_state = project_state.clone()
     1478        operation.state_forwards("test_alflpkm2m", new_state)
     1479        self.assertIsInstance(project_state.models["test_alflpkm2m", "pony"].get_field_by_name("id"), models.AutoField)
     1480        self.assertIsInstance(new_state.models["test_alflpkm2m", "pony"].get_field_by_name("id"), models.FloatField)
     1481
     1482        def assertIdTypeEqualsFkType():
     1483            with connection.cursor() as cursor:
     1484                id_type, id_null = [
     1485                    (c.type_code, c.null_ok)
     1486                    for c in connection.introspection.get_table_description(cursor, "test_alflpkm2m_pony")
     1487                    if c.name == "id"
     1488                ][0]
     1489                fk_type, fk_null = [
     1490                    (c.type_code, c.null_ok)
     1491                    for c in connection.introspection.get_table_description(cursor, "test_alflpkm2m_pony_stables")
     1492                    if c.name == "pony_id"
     1493                ][0]
     1494            self.assertEqual(id_type, fk_type)
     1495            self.assertEqual(id_null, fk_null)
     1496
     1497        assertIdTypeEqualsFkType()
     1498        # Test the database alteration
     1499        with connection.schema_editor() as editor:
     1500            operation.database_forwards("test_alflpkm2m", editor, project_state, new_state)
     1501        assertIdTypeEqualsFkType()
     1502        # And test reversal
     1503        with connection.schema_editor() as editor:
     1504            operation.database_backwards("test_alflpkm2m", editor, new_state, project_state)
     1505        assertIdTypeEqualsFkType()
     1506
     1507    @skipUnlessDBFeature('supports_foreign_keys')
     1508    def test_alter_field_pk_m2m_through(self):
     1509        """
     1510        Tests the AlterField operation on primary keys changes any FKs pointing to it.
     1511        """
     1512        project_state = self.set_up_test_model("test_alflpkm2mthrough", second_model=True)
     1513
     1514        project_state = self.apply_operations("test_alflpkm2mthrough", project_state, operations=[
     1515            migrations.CreateModel("PonyStables", fields=[
     1516                ("pony", models.ForeignKey('test_alflpkm2mthrough.Pony', models.CASCADE)),
     1517                ("stable", models.ForeignKey('test_alflpkm2mthrough.Stable', models.CASCADE)),
     1518            ]),
     1519            migrations.AddField(
     1520                "Pony", "stables",
     1521                models.ManyToManyField("Stable", related_name="ponies", through='test_alflpkm2mthrough.PonyStables')
     1522            )
     1523        ])
     1524        self.assertTableExists("test_alflpkm2mthrough_ponystables")
     1525
     1526        # Test the state alteration
     1527        operation = migrations.AlterField("Pony", "id", models.FloatField(primary_key=True))
     1528        new_state = project_state.clone()
     1529        operation.state_forwards("test_alflpkm2mthrough", new_state)
     1530        self.assertIsInstance(project_state.models["test_alflpkm2mthrough", "pony"].get_field_by_name("id"), models.AutoField)
     1531        self.assertIsInstance(new_state.models["test_alflpkm2mthrough", "pony"].get_field_by_name("id"), models.FloatField)
     1532
     1533        def assertIdTypeEqualsFkType():
     1534            with connection.cursor() as cursor:
     1535                id_type, id_null = [
     1536                    (c.type_code, c.null_ok)
     1537                    for c in connection.introspection.get_table_description(cursor, "test_alflpkm2mthrough_pony")
     1538                    if c.name == "id"
     1539                ][0]
     1540                fk_type, fk_null = [
     1541                    (c.type_code, c.null_ok)
     1542                    for c in connection.introspection.get_table_description(cursor, "test_alflpkm2mthrough_ponystables")
     1543                    if c.name == "pony_id"
     1544                ][0]
     1545            self.assertEqual(id_type, fk_type)
     1546            self.assertEqual(id_null, fk_null)
     1547
     1548        assertIdTypeEqualsFkType()
     1549        # Test the database alteration
     1550        with connection.schema_editor() as editor:
     1551            operation.database_forwards("test_alflpkm2mthrough", editor, project_state, new_state)
     1552        assertIdTypeEqualsFkType()
     1553        # And test reversal
     1554        with connection.schema_editor() as editor:
     1555            operation.database_backwards("test_alflpkm2mthrough", editor, new_state, project_state)
     1556        assertIdTypeEqualsFkType()
     1557
     1558
    14611559    def test_alter_field_reloads_state_on_fk_target_changes(self):
    14621560        """
    14631561        If AlterField doesn't reload state appropriately, the second AlterField
Back to Top