如何在 Django 模型中集成 postgresql 10/11 声明性 table 分区(即 PARTITION BY 子句)?

How to integrate postgresql 10/11 declarative table partitioning (i.e. PARTITION BY clause) in a Django model?

PostgreSQL 10 引入了 declarative table partitioningPARTITION BY 子句,我想将它用于 Django 模型。

原则上,我需要做的就是在 Django ORM 创建的 CREATE TABLE 语句末尾引入 PARTITION BY 子句。

CREATE TABLE measurement (
    city_id         int not null,
    logdate         date not null,
    peaktemp        int,
    unitsales       int
) PARTITION BY RANGE (logdate);

是否可以将此子句插入模型中?我认为也许有一种方法可以以某种方式将自定义 SQL 附加到 ORM 生成的查询中,例如使用元数据:

class Measurement(models.Model):
    ...
    class Meta:
        append = "PARTITION BY RANGE (logdate)"

据我所知,上述情况是不可能的。我也查看了 architect 库,但它没有使用新的 PARTITION BY 子句。相反,它使用继承和触发器,因此代码没有建议我可以附加子句的任何方式(对于其他数据库也没有,例如 MySQL)。

我还通过添加 ALTER TABLE... 操作来自定义迁移,例如:

operations = [
    migrations.RunSQL(
        "ALTER TABLE measurement PARTITION BY RANGE (logdate)",
    ),
] 

不幸的是,PostgreSQL 似乎不支持上述(或类似的)ALTER TABLE statement(至少现在还没有)。

最后一个想法是在发送查询之前检索 Django 模型生成的 CREATE TABLE 语句,例如sql = Measurement.get_statement() 其中 Measurement 是模型。然后,我可以附加 PARTITION BY 子句,并直接发送查询。我找不到 returns 语句的任何方法。我浏览了 Django create_model code and the sql is generated and directly send to the database,因此从那里提取语句并不容易。

有没有人知道如何以一种我仍然可以利用 Django ORM 的好处的方式实现这一点?

我建议尝试的一种方法是使用 SQL 捕获模式编辑器来收集执行 create_model 所需的 SQL。顺便说一句,这就是 the sqlmigrate feature 的力量。

from django.db.migrations import CreateModel

class CreatePartitionedModel(CreateModel):
    def __init__(self, name, fields, partition_sql, **kwargs):
        self.partition_sql = partition_sql
        super().__init__(name, fields, **kwargs)

    def database_forwards(self, app_label, schema_editor, from_state, to_state):
        collector = type(schema_editor)(
            schema_editor.connection, collect_sql=True, atomic=False
        )
        with collector:
            super().database_forwards(
                app_label, collector, from_state, to_state
            )
        collected_sql = collector.collected_sql
        schema_editor.deferred_sql.extend(
            collector.deferred_sql
        )

        model = to_state.apps.get_model(app_label, self.name)
        create_table = 'CREATE TABLE %s' % schema_editor.quote_name(
            model._meta.db_table
        )
        for sql in collected_sql:
            if str(sql).startswith(create_table):
                sql = '%s PARTITION BY %s' % (sql.rstrip(';'), self.partition_sql)
            schema_editor.execute(sql)

从那时起,您只需将 makemigrations 自动生成的 CreateModel 替换为 CreatePartitionedModel 操作,并确保指定 partition_sql='RANGE (logdate)'