From: Alan K. <sql...@xh...> - 2004-10-15 22:48:54
|
[Alan Kennedy] >> I'm trying to pickle SQLObjects, so that I can store them in >> structured documents. As far as I can see, pickles only need to >> contain the name of the SQLObject class, and the id which uniquely >> identifies the instance. [Brian Ray] > I thought SQLObject does not support pickleing because it uses pickel > for persistance: > > http://sourceforge.net/mailarchive/message.php?msg_id=7578480 Thanks for pointing out that reference Brian, I hadn't seen it before. Looking at Ian's message at that url, it looks like he's talking about support for pickling the entire SQLObject, which I would imagine would have complex consequences when it comes to connections, etc, because object identity seems to be related to the connection that the object came from. However, I'm using the pickle protocol to do something different, which is to pickle a *reference* to an SQLObject, so that when the reference is unpickled, the original object can be retrieved. So it's much less complex than pickling the entire object. On reading further about the pickle protocol, I see that it does have support for pickling references to external objects, i.e. objects that are going to be serialised by reference. This is done through the persistent_id and persistent_load methods of Pickler and Unpickler objects. http://docs.python.org/lib/node69.html I've implemented that (code below), and it works just fine, although the mechanism is a little messy. For example, I don't like having to map an SQLObject class definition from a string, which could get messy if there are multiple modules and imports involved. But that's the fault of the pickle protocol, not SQLObject. Problem for me is that I'm actually trying to do this with David Mertz gnosis.xml.pickle, which also uses the pickle protocol, i.e. __getstate__, etc. But unfortunately gnosis.xml.pickle doesn't seem to support the persistent extensions to the pickle protocol. Maybe I'll have to hack it (gnosis.xml.pickle). Seems like a lot of hard work for such a simple thing :-( Anyway, here is code that successfully pickles and unpickles *references* to SQLObjects, should anyone be interested. #source --------------------- import pickle import sqlobject import StringIO __connection__ = sqlobject.connectionForURI('sqlite:/data/database.db') class SQLObjectPickler(pickle.Pickler): def persistent_id(self, obj): if isinstance(obj, sqlobject.SQLObject): return "%s:%d" % (obj.__class__.__name__, obj.id) return None class SQLObjectUnpickler(pickle.Unpickler): def persistent_load(self, obj_str): klass_name, obj_id_str = obj_str.split(':') klass = eval(klass_name) return klass.get(int(obj_id_str)) class MoSQLObject(sqlobject.SQLObject): name = sqlobject.StringCol() if __name__ == "__main__": MoSQLObject.createTable(ifNotExists=True) obj = MoSQLObject(name='alan') buffer = StringIO.StringIO() pickler = SQLObjectPickler(buffer, 0) print "Object is %s" % str(obj) pickler.dump(obj) pickled = buffer.getvalue() print "Pickled object is %d bytes" % len(pickled) unpickler = SQLObjectUnpickler(StringIO.StringIO(pickled)) unpickled = unpickler.load() print "Unpickled object is %s" % str(unpickled) #source --------------------- which outputs #output --------------------- Object is <MoSQLObject 1 name='alan'> Pickled object is 16 bytes Unpickled object is <MoSQLObject 1 name='alan'> #output --------------------- Regards, Alan. |