我有一个PostgreSQL物化视图,可以计算一些有关制造商的数据。
我创建了一个SQLAlchemy自定义DDL命令来刷新视图:
from sqlalchemy.schema import DDLElement
from sqlalchemy.ext import compiler
class RefreshMaterializedView(DDLElement):
'''Target expected to be a view name string'''
def __init__(self, concurrently):
self.concurrently = concurrently
@compiler.compiles(RefreshMaterializedView)
def compile(element, compiler, **kw):
if element.concurrently:
return "REFRESH MATERIALIZED VIEW CONCURRENTLY %s" % (element.target)
return "REFRESH MATERIALIZED VIEW %s" % (element.target)
class ManufacturerMaterializedView(db.Model):
@classmethod
def refresh(cls, concurrently=True, bind=db.session):
RefreshMaterializedView(concurrently).execute(
target=cls.__table__.fullname, bind=bind)
以下是我目前在代码中使用它的方式:
db.session.add(new_manufacturer_instance)
ManufacturerMaterializedViewClass.refresh() # bound to the same session
db.session.flush()
# some other stuff
db.session.add(another_manufacturer_instance) # still in the same PostgreSQL transaction
ManufacturerMaterializedViewClass.refresh()
db.session.commit()
期望的行为:
ManufacturerMaterializedViewClass.refresh()
,但刷新只会在所有INSERT
/ {{1后的会话结束时发出一次已发出所有对象的/ UPDATE
语句。其他对象类型会影响此物化视图的输出,因此需要在修改这些对象后发出此刷新语句。以下是使用DELETE
查看Flask-Sqlalchemy查询日志时当前发生的情况:
SQLALCHEMY_ECHO = True
正如您所看到的,调用$ python manage.py shell
>>> ManufacturerFactory() # creates a new manufacturer instance and adds it to the session
<Manufacturer #None:'est0'>
>>> ManufacturerMV.refresh()
2015-11-29 13:33:44,811 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2015-11-29 13:33:44,812 INFO sqlalchemy.engine.base.Engine REFRESH MATERIALIZED VIEW CONCURRENTLY manufacturer_mv
2015-11-29 13:33:44,812 INFO sqlalchemy.engine.base.Engine {}
>>> db.session.flush()
2015-11-29 13:34:13,745 INFO sqlalchemy.engine.base.Engine INSERT INTO manufacturer (name, website, logo, time_updated) VALUES (%(name)s, %(website)s, %(logo)s, %(time_updated)s) RETURNING manufacturer.id
2015-11-29 13:34:13,745 INFO sqlalchemy.engine.base.Engine {'logo': '/static/images/16-rc_gear_essential.jpg', 'website': 'http://hermann.com/', 'time_updated': None, 'name': 'est0'}
>>>> db.session.commit()
2015-11-29 13:42:58,160 INFO sqlalchemy.engine.base.Engine COMMIT
会立即向数据库发出SQL,甚至在refresh()
之前,会抢占任何其他插入/更新语句。但是,在session.flush()
关闭事务之前,DDL实际上并不是由PostgreSQL执行的。
我应该如何修改我的DDL / classmethod以达到我想要的行为?
我查看了ORM events,但不太确定如何在我的用例中利用它们。我不希望在我的应用程序发出的每session.commit()
个时调用一次刷新。刷新此特定视图是一项相当昂贵的操作,因此只有当我在当前事务/ session.commit()
内实际调用refresh()
时才会发生。