On Aug 25, 2011, at 10:22 AM, Jaimy Azle wrote:

> On Thursday, August 25, 2011, 7:36:53 PM, Michael Bayer wrote:
> 
> Thanks Michael,
> 
>> The Session is not usable in the way you're using it inside of
>> after_insert and after_delete - in particular the modifications made
>> to the state of the object that was just inserted will be discarded,
>> and the add() will have no effect as the flush plan cannot be
>> changed in these events.
> 
> Still, there is an odd issue here, those mapper event only triggered
> on first instance only.

I would ask why you believe that's true - from what I can tell, you're using 
the Session.add() or something as a means to test that the event is triggered, 
which as I mentioned will have side effects.

A proper test as below indicates two inserts for Detail, one insert for Master, 
as expected:

import collections
total_insert_calls = collections.defaultdict(int)
total_delete_calls = collections.defaultdict(int)

def model_after_insert(mapper, connection, target):
    total_insert_calls[mapper.class_] += 1

def model_after_delete(mapper, connection, target):
    total_delete_calls[mapper.class_] += 1

...

assert total_insert_calls[Master] == 1
assert total_insert_calls[Detail] == 2
assert not total_delete_calls

script is attached.

> 
>>> dt_1 = Detail(1, 1, 'This is detail')
>>> dt_2 = Detail(1, 2, 'This is detail')
>>> session.add(dt_1)
>>> session.add(dt_2)
>>> session.commit()  # only the first instance call after_insert
>>>                  # event, not all
> 
>> To modify the Session's flush plan within a flush event, use the
>> before_flush() session event.
> 
> How to retrieve affected instances with their state (being inserted,
> updated, or deleted) that belong to a flush operation? for this case I
> need to validate each instance after they were inserted or before they
> were deleted from a persistence storage and adjust some rows from
> another table.

Within before_flush() you work with the session.new, session.dirty, and 
session.deleted collections, as well as with attributes.get_history() to look 
at individual attribute changes.   Any of the versioning examples on the wiki 
at http://www.sqlalchemy.org/trac/wiki/UsageRecipes illustrate these techniques.

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

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

Base = declarative_base()
engine = create_engine('sqlite:///:memory:')
Session = sessionmaker(bind=engine)


import collections
total_insert_calls = collections.defaultdict(int)
total_delete_calls = collections.defaultdict(int)

def model_after_insert(mapper, connection, target):
    total_insert_calls[mapper.class_] += 1

def model_after_delete(mapper, connection, target):
    total_delete_calls[mapper.class_] += 1

event.listen(mapper, 'after_insert', model_after_insert)
event.listen(mapper, 'after_delete', model_after_delete)

class Master(Base):
 __tablename__ = 'master'
 id = Column(Integer, primary_key=True)
 name = Column(String)
 total = Column(Integer)

 def __init__(self, name):
   self.name = name
   self.total = 0

class Detail(Base):
 __tablename__ = 'detail'
 id = Column(Integer, primary_key=True)
 master = Column(Integer, primary_key=True)
 name = Column(String)

 def __init__(self, id, master, name):
   self.id = id
   self.name = name
   self.master = master

klass = Master

Base.metadata.create_all(engine)
session = Session()

master = Master('hello world')
session.add(master)
session.commit()

dt_1 = Detail(1, 1, 'This is detail')
dt_2 = Detail(1, 2, 'This is detail')
session.add(dt_1)
session.add(dt_2)
session.commit()

assert total_insert_calls[Master] == 1
assert total_insert_calls[Detail] == 2
assert not total_delete_calls

master = session.query(Master).filter_by(id = 1).first()
print('total ', master.total)

Reply via email to