Thanks for the quick answer. On my small example, retrieving the objects from the session.new and session.dirty lists works fine in the after_flush_postexec event. But in my web application, the lists are already empty in that event.
I ended: 1. Still retrieving the objects from the session.new and session.dirty lists in before_flush event 2. Calling obj.to_dict() in the after_flush_postexec event and saving that representation 3. Using that representation in the after_commit event Thanks for the help! Le mercredi 10 octobre 2018 23:46:25 UTC+2, Mike Bayer a écrit : > > On Wed, Oct 10, 2018 at 4:40 PM Benjamin Bertrand <bee...@gmail.com > <javascript:>> wrote: > > > > Hi, > > > > I'm trying to index some data from my postgres database to > elasticsearch. I do something similar to what is described in this blog > post: > https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-xvi-full-text-search > > > I store the new or modified objects in the before_flush event and want > to store a representation of the object in the after_commit event. > > > > My issue is that I have some relationships in my objects and the > following exception is raised by sqlalchemy: > > sqlalchemy.exc.InvalidRequestError: This session is in 'committed' > state; no further SQL can be emitted within this transaction. > > so you need to disable expire_on_commit, since you want the data there > inside of after_commit there is no way around this: > > session = Session(engine, expire_on_commit=False) > > it's likely you might want to use a different event hook for what > you're trying to do as the above is not ideal. after_flush_postexec() > might be a better choice as that is prior to the expiration. > > > then you need to load all the attributes somewhere ahead of time, > here's doing it inside of before_flush: > > @event.listens_for(Session, "before_flush") > def before_flush(session, flush_context, instances): > # ... > if isinstance(obj, (Device, Host)): > # ... > > for attr in inspect(obj).attrs: > getattr(obj, attr.key) > > where again, you can query the database inside of > after_flush_postexec() without issue and no pre-load is needed. If > you are trying to only print at the after_commit boundary, then place > the state you want into your session._changes, like the actual strings > you're looking to print, then just print those. > > > > > > > > Here is a short example to illustrate the issue: > > > > import itertools > > from sqlalchemy import event, Column, Integer, String, ForeignKey, > create_engine > > from sqlalchemy.orm import Session, relationship > > from sqlalchemy.ext.declarative import declarative_base > > > > > > Base = declarative_base() > > > > > > @event.listens_for(Session, "before_flush") > > def before_flush(session, flush_context, instances): > > session._changes = [] > > for obj in itertools.chain(session.new, session.dirty): > > print(f"before_flush: {obj}") > > if isinstance(obj, (Device, Host)): > > session._changes.append(obj) > > > > > > @event.listens_for(Session, "after_commit") > > def after_commit(session): > > print("after_commit") > > for obj in session._changes: > > print(obj.to_dict()) > > > > > > class User(Base): > > __tablename__ = "user_account" > > id = Column(Integer, primary_key=True) > > username = Column(String, nullable=False, unique=True) > > > > def __str__(self): > > return self.username > > > > > > class Device(Base): > > __tablename__ = "device" > > id = Column(Integer, primary_key=True) > > name = Column(String) > > user_id = Column(ForeignKey("user_account.id"), nullable=False, > default=1) > > host_id = Column(Integer, ForeignKey("host.id")) > > user = relationship("User") > > > > def __str__(self): > > return self.name > > > > def to_dict(self): > > return { > > "id": self.id, > > "name": self.name, > > "user": str(self.user), > > "host": str(self.host), > > } > > > > > > class Host(Base): > > __tablename__ = "host" > > id = Column(Integer, primary_key=True) > > name = Column(String) > > devices = relationship("Device", backref="host") > > > > def __str__(self): > > return self.name > > > > def to_dict(self): > > return { > > "id": self.id, > > "name": self.name, > > "devices": [device.to_dict() for device in self.devices], > > } > > > > > > # engine = create_engine("postgresql://postgres:secret@localhost/test", > echo=True) > > engine = create_engine("postgresql://postgres:secret@localhost/test") > > Base.metadata.drop_all(engine) > > Base.metadata.create_all(engine) > > session = Session(engine) > > > > user = User(id=1, username="John") > > session.add(user) > > session.commit() > > > > # If I don't query the user to add it to the session, I get: > > # sqlalchemy.exc.InvalidRequestError: > > # This session is in 'committed' state; no further SQL can be emitted > within this transaction. > > myuser = session.query(User).get(1) > > device1 = Device(name="mydevice") > > session.add(device1) > > session.commit() > > > > host1 = Host(name="test") > > session.add(host1) > > # This raises sqlalchemy.exc.InvalidRequestError when trying to access > host1.devices > > session.commit() > > > > > > How can I make sure that all relationships are in the session before the > after_commit event? > > > > -- > > SQLAlchemy - > > The Python SQL Toolkit and Object Relational Mapper > > > > http://www.sqlalchemy.org/ > > > > To post example code, please provide an MCVE: Minimal, Complete, and > Verifiable Example. See http://stackoverflow.com/help/mcve for a full > description. > > --- > > You received this message because you are subscribed to the Google > Groups "sqlalchemy" group. > > To unsubscribe from this group and stop receiving emails from it, send > an email to sqlalchemy+...@googlegroups.com <javascript:>. > > To post to this group, send email to sqlal...@googlegroups.com > <javascript:>. > > Visit this group at https://groups.google.com/group/sqlalchemy. > > For more options, visit https://groups.google.com/d/optout. > -- SQLAlchemy - The Python SQL Toolkit and Object Relational Mapper http://www.sqlalchemy.org/ To post example code, please provide an MCVE: Minimal, Complete, and Verifiable Example. See http://stackoverflow.com/help/mcve for a full description. --- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. Visit this group at https://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.