expunge not fully effective?

Issue #861 resolved
Former user created an issue

Hi.

The attached code works with SQLAlchemy 0.3, but with SQLAlchemy 0.4 (from trunk) I get:

Traceback (most recent call last):
  File "orm_expunge.py", line 67, in ?
    transact(db, update, obj)
  File "orm_expunge.py", line 27, in transact
    ret = f(conn, sess, *args, **kwargs)
  File "orm_expunge.py", line 54, in update
    sess.save(oldobj)
  File "/home/manlio/projects/svn-external/sqlalchemy/trunk/lib/sqlalchemy/orm/session.py", line 794, in save
    self._save_impl(object, entity_name=entity_name)
  File "/home/manlio/projects/svn-external/sqlalchemy/trunk/lib/sqlalchemy/orm/session.py", line 962, in _save_impl
    raise exceptions.InvalidRequestError("Instance '%s' is already persistent" % mapperutil.instance_str(obj))
sqlalchemy.exceptions.InvalidRequestError: Instance 'Test@-0x4882bab4' is already persistent

Comments (1)

  1. Mike Bayer repo owner

    sorry, the test script is incorrect; save() is not meant to be used for persistent objects; use update() or save_or_update(). this is a recent change in trunk so that people dont get confused by the meaning of save() and as I've been telling people I might replace all three of save/update/save_or_update with a single "add()" method. below, we assert that the expunge worked and also use update():

    from sqlalchemy import engine, schema, orm, types
    
    
    metadata = schema.MetaData()
    
    test = schema.Table(
        'test', metadata,
        schema.Column('id', types.Integer, primary_key=True),
        schema.Column('x', types.String)
        )
    
    
    class Test(object):
        def __init__(self, id, x):
            self.id = id
            self.x = x
    
    
    test_mapper = orm.mapper(Test, test)
    
    
    def transact(db, f, *args, **kwargs):
        conn = db.connect()
        trans = conn.begin()
        sess = orm.create_session(bind=conn)
    
        ret = f(conn, sess, *args, **kwargs)
    
        sess.flush()
        sess.close()
        trans.commit()
        conn.close()
    
        return ret
    
    
    def insert(conn, sess):
        obj = Test(0, 'test')
        sess.save(obj)
    
    def select(conn, sess):
        obj = sess.get(test_mapper, 0)
    
        obj.x = 'x'
    
        assert obj in sess
    
        # Do not update the database now
        sess.expunge(obj)
    
        assert obj not in sess
    
        return obj
    
    def update(conn, sess, obj):
        oldobj = sess.get(test_mapper, 0)
        oldobj.x = obj.x
    
        sess.update(oldobj)
    
    
    URL = 'sqlite://'
    db = engine.create_engine(URL, echo=False)
    metadata.bind = db
    
    
    try:
        metadata.create_all()
    
        transact(db, insert)
        obj = transact(db, select)
        transact(db, update, obj)
    finally:
        metadata.drop_all()
    
  2. Log in to comment