Hi all,
I’ve recently discovered that the order in which events are fired for 
individual instances is not deterministic. For example, if I add several 
Author instances to an existing Book then commit the changes, the Book‘s 
post_update may run before or after the Author‘s post_insert, and the same 
code might produce different results on successive runs.

Here’s some example code (full gist with models is here: 
https://gist.github.com/chadrik/c3fff520bd41a48f0a57):

@event.listens_for(Book, 'after_update')def book_after_update(mapper, 
connection, instance):
    print "after_update", instance, instance.authors
@event.listens_for(Book, 'after_insert')def book_after_insert(mapper, 
connection, instance):
    print "after_insert", instance, instance.authors
@event.listens_for(Author, 'after_insert')def author_after_insert(mapper, 
connection, instance):
    print "after_insert", instance
def test():
    Base.metadata.create_all(engine)

    book = Book(title='sqlalchemy for dummies')
    session = Session()
    session.add(book)
    session.commit()

    john = Author(name='John Doe')
    jane = Author(name='Jane Doe')
    book.authors = [john, jane]
    book2 = Book(title='sqlalchemy pro tips')
    book2.authors = [john]
    session.add(book2)
    session.commit()
if __name__ == '__main__':
    test()

running test() might print:

after_insert Book(1) []
after_insert Author(1)
after_insert Author(2)
after_insert Book(2) [Author(1)]
after_update Book(1) [Author(1), Author(2)]

or it might print:

after_insert Book(1) []
after_insert Book(2) [Author(None)]
after_update Book(1) [Author(None), Author(None)]
after_insert Author(1)
after_insert Author(2)

it can be tricky to consistently reproduce the behavior. sometimes 
successive runs change randomly with no intervention, sometimes it requires 
changing a single line of unrelated code, like a comment (I guess this has 
something to do with python’s byte-compiling and hashing). sometimes a test 
that will run consistently in the same order using python -m "sqlbooks" 
will run consistently in a different order using python -c "import 
sqlbooks;sqlbooks.test()"

setting __mapper_args__ = {'batch': False} did not help.

it seems that internally sqlalchemy is using unordered containers like 
dicts or sets when managing the instances passed to these events. my 
question is: is it possible to provide a meaningful and consistent order to 
these events? 

ideally, for my case, I’m hoping to have the Author’s post_insert run 
before the Book’s post_update, such that the Author’s auto-incrementing 
id’s are available (you’ll notice in the two output blocks that I pasted, 
that in the second it prints Author(None)) where None should be the integer 
id. Also note that the relationship between Book and Author is many-to-many 
and handled by a secondary table, so no data is persisted to the db for the 
book table. I assume the post_update is purely to notify me that the 
Book.authors relationship changed, and as such, it would be highly 
convenient if the items which were added to the relationship were already 
inserted and had their primary keys.

thanks,
chad

​
​

-- 
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 [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to