after_bulk_update table/column info?

307 views
Skip to first unread message

David Szotten

unread,
Jul 7, 2013, 5:51:53 AM7/7/13
to sqlal...@googlegroups.com
Hi,

Is it possible to find out which class/columns were included in the update from an `after_bulk_update` event handler?

From what i can tell from the source, this information lives on the `BulkUpdate(Evaluate in my case)` object which isn't passed to the event handler, and I can't figure out how to access this info from the available variables (session, query, query_context, result)

Thanks
-d

Michael Bayer

unread,
Jul 7, 2013, 12:14:27 PM7/7/13
to sqlal...@googlegroups.com
this should probably be available as some documented helper function for now, it's tricky to figure out up front what parameters these events will need.  Really these "bulk" events should get to know all the arguments that were passed, including the synchronize_session argument which isn't present at all right now.

that particular info you can get like this:

@event.listens_for(Session, "after_bulk_update")
def bulk_ud(sess, query, ctx, result):
    print result.context.compiled.params





--
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.
To post to this group, send email to sqlal...@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

David Szotten

unread,
Jul 7, 2013, 1:32:44 PM7/7/13
to sqlal...@googlegroups.com
Awesome, thanks!

(For the record, i was ultimately looking for the field types involved in the update, which i found with your help above as:

    for bind in result.context.compiled.binds.values():
        field_type = bind.type
        if isinstance(field_type, MyField):
            raise NotImplementedError("Bulk updates are not yet supported")


Thanks again!



--
You received this message because you are subscribed to a topic in the Google Groups "sqlalchemy" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/sqlalchemy/SwOckp6-Vh8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to sqlalchemy+...@googlegroups.com.
Message has been deleted

clarkie

unread,
Jul 24, 2014, 1:46:17 PM7/24/14
to sqlal...@googlegroups.com
Hi,

I am trying to do something similar - 
But instead of using before_flush I want to use after_bulk_update. This is because we use update query in our code and before_flush is not called for bulk updates. 

http://stackoverflow.com/questions/14896982/sqlalchemy-versioned-object-not-working 

I couldn't find a straight fwd way to check if state of the object (is dirty?) or check if it is versioned (like in the example above) using the session object parameter of after_bulk_insert. I looked at "result.context.compiled" I didn't find anything that will give me this info.

Has anybody done this? Appreciate any help...

thanks.

Michael Bayer

unread,
Jul 24, 2014, 2:46:47 PM7/24/14
to sqlal...@googlegroups.com
On Jul 24, 2014, at 1:46 PM, clarkie <f200...@gmail.com> wrote:

Hi,

I am trying to do something similar - 
But instead of using before_flush I want to use after_bulk_update. This is because we use update query in our code and before_flush is not called for bulk updates. 

http://stackoverflow.com/questions/14896982/sqlalchemy-versioned-object-not-working 

I couldn't find a straight fwd way to check if state of the object (is dirty?) or check if it is versioned (like in the example above) using the session object parameter of after_bulk_insert. I looked at "result.context.compiled" I didn't find anything that will give me this info.

Has anybody done this? Appreciate any help...



this system relies on in-Python checking and manipulation of state.  if you want to use an UPDATE method, then you’d have to also emit a SELECT for all of those objects, pull them in, and create the versioned data as needed.  there’s ways to optimize this, and even potentially to emit a special INSERT from SELECT to do it all at once on the database side, that is doable probably with caveats.  But if you aren’t pulling objects in locally you’re just dealing with SQL, not really anything ORM related.   There is no concept of “dirty” or “the object” for a row that you’re updating on the DB side without any corresponding entry locally.   If you want to get at the query that is being run, within after_bulk_update() you can access the Query object itself from the given context, and run all() on it.


clarkie

unread,
Jul 24, 2014, 3:24:13 PM7/24/14
to sqlal...@googlegroups.com
Thanks for your quick response. Sorry, my question was not very clear.
In this I want to replace the before_flush with after_bulk_update - since both have session object I expect everything else the work as is, but it doesnt. Both the session objects look different in a debugger. How do I get the create_version to work with the after_bulk_update parameters (session, query, query_context, result)?

Below code is what is triggering the after_bulk_update -

db.session.query(someClass).filter_by(**filter).update(data)
db.session.flush()

Michael Bayer

unread,
Jul 24, 2014, 6:05:43 PM7/24/14
to sqlal...@googlegroups.com
I think I got your question fully, and I know what example you’re using.

The example relies on SQL rows being represented by objects that are in memory in Python.  When you use query().update(), this is not the case; a SQL string is emitted to the database and there doesn’t need to be any corresponding object in memory.  Therefore the approach illustrated by the example needs an entirely different system of operating in this mode.  All of the work performed by create_version() assumes the object is present locally.

The most expedient way to accomplish this would be, in the after_bulk_update() method, to load the objects represented by using the Query to emit a SELECT:

    @event.listens_for(session, ‘after_bulk_update')
    def update(context):
        for obj in verisioned_objects(context.query.all()):
            create_version(obj, context.session)

however, this approach negates any performance gains achieved by using query.update(), which is typically a leading rationale for the use of this method.    You’d at least want to try to see what class the update statement was against, check that it has a “__history_mapper__” attribute.  To see what class the statement is against, use context.query.column_descriptions[0][‘type’] (see http://docs.sqlalchemy.org/en/latest/orm/query.html?highlight=column_descriptions#sqlalchemy.orm.query.Query.column_descriptions).







On Jul 24, 2014, at 3:24 PM, clarkie <f200...@gmail.com> wrote:

Thanks for your quick response. Sorry, my question was not very clear.
-- 
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 tosqlalchemy+...@googlegroups.com.
To post to this group, send email to sqlal...@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

clarkie

unread,
Jul 24, 2014, 6:34:50 PM7/24/14
to sqlal...@googlegroups.com
Great! yes, your comments made more sense after reading more of the extensive documentation. thanks again..I ended up doing the same with a slight variation -

@event.listens_for(session, ‘after_bulk_update')
    def after_bulk_update(session, query, query_context, result): (still using 0.7)
        for obj in versioned_objects([obj for obj in session]):
            create_version(obj, session)
It seems to be producing the expected results. However there is no history for the obj (obviously for the same reason you explained below). I am now doing this check manually using the database (latest) record. Is there a better way?

I have multiple column updates so query.update makes more sense (I think?)
Reply all
Reply to author
Forward
0 new messages