Reputation: 3
simple code like:
import pickle, cPickle
from app import session, redis
class MyObj(DeclarativeBase):
@classmethod
def get(cls,id):
key = cls.__name__+":"+str(id)
cached = redis.get(key)
if cached:
# unserialize to cls instance
return cPickle.loads(cached)
record = session.query(cls).filter(cls.id==id).one()
if record:
# serialize and store to redis
redis.set(key, pickle.dumps(record))
return record
# first time , a normal orm instance returned from session query (uncached)
obj = MyObj.get(1)
# but the next requests, get cached from redis.
# and i want to delete the record
delobj = MyObj.get(1)
session.delete(delobj)
session.commit()
it raise an error like "instance xxx is not persisted"
i tried session.add(delobj) before delete, the Pending state is True ( state = inspect(delobj) ), but it still doesn't work for deleting.
Upvotes: 0
Views: 3373
Reputation: 52949
You need to merge cached instances to current session:
merge()
transfers state from an outside object into a new or already existing instance within a session.
From examples:
An application is storing objects in an in-memory cache, shared by many
Session
objects simultaneously.merge()
is used each time an object is retrieved from the cache to create a local copy of it in each Session which requests it. The cached object remains detached; only its state is moved into copies of itself that are local to individual Session objects.In the caching use case, it’s common to use the
load=False
flag to remove the overhead of reconciling the object’s state with the database. There’s also a “bulk” version ofmerge()
calledmerge_result()
that was designed to work with cache-extended Query objects - see the section Dogpile Caching.
So:
if cached:
# unserialize to cls instance
return session.merge(cPickle.loads(cached), load=False)
Remember to cache persisted objects of cls
only, or you'll get the error:
InvalidRequestError: merge() with load=False option does not support objects transient (i.e. unpersisted) objects. flush() all changes on mapped instances before merging with load=False.
A simple way to produce the aforementioned error:
obj = MyObj()
cached = pickle.dumps(obj)
obj2 = session.merge(pickle.loads(cached), load=False)
...
InvalidRequestError: merge() with load=False option does not support objects transient (i.e. unpersisted) objects. flush() all changes on mapped instances before merging with load=False.
If on the other hand you first properly persist the object:
In [63]: obj = MyObj()
In [64]: session.add(obj)
In [65]: session.flush()
In [66]: obj.__dict__
Out[66]:
{'id': 2,
'_sa_instance_state': <sqlalchemy.orm.state.InstanceState at 0x7fe6f380cac8>}
In [67]: cached = pickle.dumps(obj)
In [68]: session.commit()
In [69]: obj2 = session.merge(pickle.loads(cached), load=False)
In [70]: obj2 in session
Out[70]: True
Upvotes: 1