Django 在迁移中添加外键约束后添加运行 SQL 操作

Django add RunSQL operation after adding ForeignKey constraint in migration

我正在使用 Django 2.1Python 3.6MySQL 8 .

数据库非常庞大 table,其中包含大量大行,因此应用于此 table 的迁移需要数小时才能完成。我发现如果我从这个 table 中删除 fulltext index 它会启用 modyfing table 的 inplace alghorithm - 它更快。

所以我需要在 Django 中利用它。 我考虑过删除 fulltext index 作为第一个迁移操作,并在所有其他操作之后重新创建它。

operations = [
    migrations.CreateModel(
        name='NewModel',
        fields=[
            ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
        ],
        options={
            'abstract': False,
        },
    ),
    migrations.RunSQL(
        ('DROP INDEX fulltext_idx_content ON summarizer_model',),
        ('CREATE FULLTEXT INDEX fulltext_idx_content ON summarizer_model(content)',),
    ),
    migrations.AddField(
        model_name='model',
        name='new_model',
        field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='new_models_objects', to='summarizer.new_model'),
    ),
    migrations.RunSQL(
        ('CREATE FULLTEXT INDEX fulltext_idx_content ON summarizer_model(content)',),
        ('DROP INDEX fulltext_idx_content ON summarizer_model',),
    ),
]

(我很快 'anonymized' 上面的代码片段,所以如果有一些逻辑错误那么请原谅 - 这里不是这种情况:))

问题是 Django 迁移总是把添加 ForeignKey 约束作为最后一个操作。所以在我最后一个创建索引的 RunSQL 之后。这使得操作非常缓慢(用新列复制整个 table)。

有办法克服吗?我猜将另一个迁移文件仅用于创建索引会很奇怪。我只想在迁移时临时删除索引。

谢谢

在深入了解 Django 迁移的内部结构后,我发现 Django 中的一些迁移操作将 SQL 添加到模式对象中所谓的 deferred_sql 列表中。 所以知道解决我的问题是继承自 RunSQL 操作。 我没有立即执行 SQL,而是将其附加到 deferred_sql 列表,然后......就是这样!

class DeferredForwardRunSQL(RunSQL):
def database_forwards(self, app_label, schema_editor, from_state, to_state):
    schema_editor.deferred_sql.append(self.sql[0])

我还需要修改最后一个 运行SQL 迁移操作:

operations = [
    migrations.CreateModel(
        name='NewModel',
        fields=[
            ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
        ],
        options={
            'abstract': False,
        },
    ),
    migrations.RunSQL(
        ('DROP INDEX fulltext_idx_content ON summarizer_model',),
        ('CREATE FULLTEXT INDEX fulltext_idx_content ON summarizer_model(content)',),
    ),
    migrations.AddField(
        model_name='model',
        name='new_model',
        field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='new_models_objects', to='summarizer.new_model'),
    ),
    DeferredForwardRunSQL(
        ('CREATE FULLTEXT INDEX fulltext_idx_content ON summarizer_model(content)',),
        ('DROP INDEX fulltext_idx_content ON summarizer_model',),
    ),
]

也适用于向后