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

时间:2018-11-01 23:00:27

标签: django postgresql database-partitioning

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

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

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子句,然后直接发送查询。我找不到任何返回该语句的方法。我经历了Django create_model code,而sql是generated and directly send to the database,所以从那里提取语句并不容易。

有人知道如何以仍然可以使用Django ORM的优势实现这一目标吗?

1 个答案:

答案 0 :(得分:6)

我建议尝试的一种方法是使用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)'