Skip to content

UpdatableInsertable

zzzeek edited this page Oct 20, 2015 · 1 revision

UpdateableInsertable

The goal here is to annotate mapped Column objects with optional "updatable=False" and "insertable=False" directives; the effect will be that changes / values placed on these columns will be reverted when a flush occurs, allowing server-side directives to always take precedence over any ORM-level changes. Alternatively, the recipe can be altered to raise an exception when these conditions are detected.

from sqlalchemy import event
from sqlalchemy.orm import mapper


@event.listens_for(mapper, "before_insert", raw=True)
def before_insert(mapper, connection, target):
    for col_attr in mapper.column_attrs:
        col = col_attr.columns[0]
        if not col.info.get('insertable', True):
            target.dict.pop(col_attr.key, None)


@event.listens_for(mapper, "before_update", raw=True)
def before_update(mapper, connection, target):
    for col_attr in mapper.column_attrs:
        col = col_attr.columns[0]
        if not col.info.get('updateable', True) and \
                col_attr.key in target.committed_state:
            target.dict[col_attr.key] = target.committed_state.pop(
                col_attr.key)


if __name__ == '__main__':
    from sqlalchemy import *
    from sqlalchemy.orm import *
    from sqlalchemy.ext.declarative import declarative_base

    Base = declarative_base()

    class A(Base):
        __tablename__ = 'a'
        id = Column(Integer, primary_key=True)
        plain = Column(Integer)
        uninsertable = Column(
            Integer, info={"insertable": False}, server_default="7")
        unupdateable = Column(Integer, info={"updateable": False})

    e = create_engine("sqlite://", echo=True)
    Base.metadata.create_all(e)

    s = Session(e)

    a1 = A(plain=5, unupdateable=10)
    a2 = A(plain=6, uninsertable=18, unupdateable=10)
    s.add_all([a1, a2])
    s.flush()
    assert a2.uninsertable == 7

    s.commit()

    assert set(a for a, in s.query(A.uninsertable)) == set([7])

    a1 = s.query(A).filter_by(plain=6).one()
    a1.unupdateable = 12
    s.flush()
    assert a1.unupdateable == 10
    s.commit()

    assert set(a for a, in s.query(A.unupdateable)) == set([10])
Clone this wiki locally