Как сделать так, чтобы SQLAlchemy custom DDL генерировался после вставки объекта?

У меня есть материализованное представление PostgreSQL, которое вычисляет некоторые данные о производителях.

Я создал пользовательскую команду DDA SQLAlchemy, чтобы обновить представление:

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()

Желаемое поведение:

  1. Материализованное представление обновляется после создания new_manufacturer_instance.
  2. Я могу многократно вставить новых производителей и позвонить ManufacturerMaterializedViewClass.refresh() несколько раз в течение одного сеанса, но обновление будет выпущено только один раз, в конце сеанса после всех INSERT/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

Как видите, звонит refresh() немедленно выдает SQL в БД, даже до session.flush(), упреждая любые дополнительные операторы вставки / обновления. Тем не менее, DDL на самом деле не выполняется PostgreSQL до session.commit() закрывает сделку

Как мне изменить мой метод DDL/classtod для достижения желаемого поведения?

Я посмотрел на события ORM, но не был уверен, как использовать их для моего случая использования. Я не хочу, чтобы каждый раз приходило обновление session.commit() испускается моим заявлением. Обновление этого конкретного представления является довольно дорогой операцией, поэтому оно должно происходить только тогда, когда я действительно refresh() в рамках текущей транзакции /session,

0 ответов

Другие вопросы по тегам