SQLAlchemy declarative: defining triggers and indexes (Postgres 9)

Ominus picture Ominus · Jan 19, 2012 · Viewed 9.7k times · Source

Is there a way in the SQLAlchemy class of a table to define/create triggers and indexes for that table?

For instance if i had a basic table like ...

class Customer(DeclarativeBase):
    __tablename__ = 'customers'
    customer_id = Column(Integer, primary_key=True,autoincrement=True)
    customer_code = Column(Unicode(15),unique=True)
    customer_name = Column(Unicode(100))
    search_vector = Column(tsvector) ## *Not sure how do this yet either in sqlalchemy*.

I now want to create a trigger to update "search_vector"

CREATE TRIGGER customers_search_vector_update BEFORE INSERT OR UPDATE
ON customers
FOR EACH ROW EXECUTE PROCEDURE
tsvector_update_trigger(search_vector,'pg_catalog.english',customer_code,customer_name);

Then I wanted to add that field also as an index ...

create index customers_search_vector_indx ON customers USING gin(search_vector);

Right now after i do any kind of database regeneration from my app i have to do the add column for the tsvector column, the trigger definition, and then the index statement from psql. Not the end of the world but its easy to forget a step. I am all about automation so if I can get this all to happen during the apps setup then bonus!

Answer

van picture van · Jan 19, 2012

Indicies are straight-forward to create. For single-column with index=True parameter like below:

customer_code = Column(Unicode(15),unique=True,index=True)

But if you want more control over the name and options, use the explicit Index() construct:

Index('customers_search_vector_indx', Customer.__table__.c.search_vector, postgresql_using='gin')

Triggers can be created as well, but those need to still be SQL-based and hooked to the DDL events. See Customizing DDL for more info, but the code might look similar to this:

from sqlalchemy import event, DDL
trig_ddl = DDL("""
    CREATE TRIGGER customers_search_vector_update BEFORE INSERT OR UPDATE
    ON customers
    FOR EACH ROW EXECUTE PROCEDURE
    tsvector_update_trigger(search_vector,'pg_catalog.english',customer_code,customer_name);
""")
tbl = Customer.__table__
event.listen(tbl, 'after_create', trig_ddl.execute_if(dialect='postgresql'))

Sidenote: I do not know how to configure tsvector datatype: deserves a separate question.