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的优势实现这一目标吗?
答案 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)'
。