sqlalchemy event before_update is not getting executed

Viewed 296

I want to maintain audit log for database changes (if any update happens then the previous state to be inserted in log table

below is the code i have written

from sqlalchemy import create_engine
from sqlalchemy import event
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, DateTime
from datetime import datetime
from sqlalchemy.orm import sessionmaker

engine = create_engine('mysql+pymysql://root:p4ssw0rd@0.0.0.0:33061/error_details')
Base = declarative_base()

Base.metadata.create_all(engine)
Session = sessionmaker()
Session.configure(bind=engine)
session = Session()


class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    name = Column(String(10))

    def __repr__(self):
        return f"User(name={self.name}, id={self.id})"


class User_Log(Base):
    __tablename__ = "users_log"
    id = Column(Integer, primary_key=True)
    name = Column(String(10))
    modified_on = Column(DateTime, default=datetime.utcnow)
    action = Column(String(10))


@event.listens_for(User, 'before_update')
def update_user_log(mapper, connection, target):
    print("func() called ")


session.query(User).filter(User.id == 2).update({User.name: "ankit--"})
session.commit()

function update_user_log is not getting called

Note - I want to perform the database audit logs in sqlalchemy only

2 Answers

Update and deletes with arbitrary where clauses must be intercepted with the SessionEvents.do_orm_execute_handler. This because

The ORM-enabled UPDATE and DELETE features bypass ORM unit-of-work automation in favor being able to emit a single UPDATE or DELETE statement that matches multiple rows at once without complexity.*

You can intercept such updates with a handler like this:

@event.listens_for(Session, 'do_orm_execute')
def update_user_log(orm_execute_state):
    if orm_execute_state.is_update:
        print("func() called ")

* Quote taken from the second warning block at the end of the previously linked section.

It's not terribly clear in the docs, but merge() will cause the before_update event to fire, see Session API docs

Related