marking an object "read-only" / recursive expunge?

502 views
Skip to first unread message

Jonathan Vanasco

unread,
Jul 17, 2015, 2:18:21 PM7/17/15
to sqlal...@googlegroups.com
I'm updating our "visual preview" tool for edits, and ran into an issue.  

In order to best mimic the production view, I can't simply "clone" the objects any longer (they have way too many attributes and relationships) and must apply edits onto the real object.  

I'd like to ensure that changes can't persist, so the easiest way to mark the object "read only" was to expunge it from the session.  Perfect.

The problem I've run into, is that `expunge()` only applies to the actual object -- it doesn't apply to any of the loaded (lazy/eager/etc) relationships.  

Has anyone figured out way to do a recursive expunge?  I don't want to alter the cascades on the relationship, I just have one (or more) context(s) where I need to disassociate an object and any loaded children from the session.

Mike Bayer

unread,
Jul 17, 2015, 2:26:37 PM7/17/15
to sqlal...@googlegroups.com
there's an "expunge" cascade you can set on relationship:

http://docs.sqlalchemy.org/en/rel_1_0/orm/cascades.html#expunge





--
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/d/optout.

Jonathan Vanasco

unread,
Jul 17, 2015, 2:32:35 PM7/17/15
to sqlal...@googlegroups.com
editing the cascade isn't an option.  

for now this seems to work, though it's ugly.

        def recursive_expunge(obj, dbSession):
            def _recursive_expunge(_obj):
                if hasattr(_obj, '__mapper__'):
                    for rel in obj.__mapper__.relationships:
                        try:
                            dbSession.expunge(rel)
                        except sqlalchemy.orm.exc.UnmappedInstanceError:
                            pass
            _recursive_expunge(obj)
        recursive_expunge(postingObject, self.request.dbSession.writer)

Mike Bayer

unread,
Jul 17, 2015, 2:46:42 PM7/17/15
to sqlal...@googlegroups.com


On 7/17/15 2:32 PM, Jonathan Vanasco wrote:
editing the cascade isn't an option. 

well then yeah you need to do your own thing :)

i'd use inspect(obj) to get the mapper.    but also you might want to use cascade_iterator: http://docs.sqlalchemy.org/en/rel_1_0/orm/mapping_api.html?highlight=cascade_iterator#sqlalchemy.orm.mapper.Mapper.cascade_iterator

Jonathan Vanasco

unread,
Jul 17, 2015, 3:02:06 PM7/17/15
to sqlal...@googlegroups.com


On Friday, July 17, 2015 at 2:46:42 PM UTC-4, Michael Bayer wrote:

well then yeah you need to do your own thing :)

i foolishly thought this was something others may have experienced ;)
 
i'd use inspect(obj) to get the mapper.    but also you might want to use cascade_iterator: http://docs.sqlalchemy.org/en/rel_1_0/orm/mapping_api.html?highlight=cascade_iterator#sqlalchemy.orm.mapper.Mapper.cascade_iterator
 
i'll look into the cascade_iterator.  I keep forgetting that inspect is not just for debugging.
 

Jonathan Vanasco

unread,
Jul 23, 2015, 5:06:05 PM7/23/15
to sqlalchemy, jvan...@gmail.com
So my code above is just completely wrong.  

This code actually does what one expects:

    def recursive_expunge(obj, dbSession):
        def _recursive_expunge(_obj):
            _instance_state = sqlalchemy.inspection.inspect(_obj)
            _mapper = _instance_state.mapper
            try:
                dbSession.expunge(_obj)
                # print "expunge | %s" % _obj
            except sqlalchemy.orm.exc.UnmappedInstanceError:
                # print "sqlalchemy.orm.exc.UnmappedInstanceError | %s" % _obj
                pass
            except sqlalchemy.exc.InvalidRequestError:
                # print "sqlalchemy.exc.UnmappedInstanceError | %s" % _obj
                pass
            if _mapper:
                # _unloaded = [(_name, _rel) for (_name, _rel) in _mapper.relationships.items() if _name in _instance_state.unloaded]
                _loaded_rels = [i for i in _mapper.relationships.items() if i[0] not in _instance_state.unloaded]
                for (_name, _rel) in _loaded_rels:
                    _loaded_rel_data = getattr(_obj, _name)
                    if _loaded_rel_data:
                        if not _rel.uselist:
                            _recursive_expunge(_loaded_rel_data)
                        else:
                            for _i in _loaded_rel_data:
                                _recursive_expunge(_i)
        _recursive_expunge(obj)

Reply all
Reply to author
Forward
0 new messages