On Sep 6, 2010, at 1:04 PM, Michael Bayer wrote:

> 
> But there is good news, if we look at this for what it seems to be, which is 
> an optimized serialization scheme.  You should build it that way.   Write 
> custom serialization (__getstate__/__setstate__) for your class - if it 
> detects as transient, expire all relationship()-based attributes upon 
> __getstate__ - upon __setstate__, iterate through all relationship 
> based-attrbutes (using mapper.iterate_properties()) and plug them all into 
> query.with_parent() to again produce the "pending" attributes.    Basically, 
> take advantage of the foreign key/primary key attributes already present to 
> reduce the size of the serialization, and load the data back on the other 
> end, transparently.   It's up to __setstate__ to figure out transactional 
> context.   You could use scoped_session which is the easy way out, or write a 
> custom pickler that accepts "session" (I'd go with the latter, more 
> explicit).   Such a recipe could even look at the "local_remote_pairs" of 
> each relationship to decide which related attributes should be expired, and 
> which should not, based on whether or not the necessary FK attributes are 
> present.   It would make a great recipe for the wiki.

attached is a proof of concept  on that, using the expression serializer.   
This might not be your solution, but it is interesting (needs the latest tip 
with the working with_parent() to work).





from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm.properties import RelationshipProperty, MANYTOONE
import pickle
from sqlalchemy.orm.util import has_identity, with_parent
from sqlalchemy.ext import serializer

Base = declarative_base()

def many_to_one_props(mapper):
    for prop in mapper.iterate_properties:
        if isinstance(prop, RelationshipProperty) and \
                prop.direction == MANYTOONE:
            yield prop
            
class Parent(Base):
    __tablename__ = 'parent'
    id = Column(Integer, primary_key=True)
    related_id = Column(Integer, ForeignKey('related.id'))
    some_other_id = Column(Integer, ForeignKey('some_other.id'))
    
    related = relationship("Related")
    some_other = relationship("SomeOther")
    
    def __getstate__(self):
        if not has_identity(self):
            d = self.__dict__.copy()
            
            mapper = object_mapper(self)
            for prop in many_to_one_props(mapper):
                for l, r in prop.local_remote_pairs:
                    if mapper.get_property_by_column(l).key not in d:
                        break
                else:
                    d[prop.key] = serializer.dumps(with_parent(self, prop))
            return d
        else:
            return self.__dict__
            
    def __setstate__(self, d):
        self.__dict__.update(d)
        if not has_identity(self):
            for prop in many_to_one_props(object_mapper(self)):
                if prop.key in self.__dict__:
                    crit = serializer.loads(
                        self.__dict__[prop.key],
                        metadata = self.metadata,
                    )
                    self.__dict__[prop.key] = \
                            session.query(prop.mapper).filter(crit).one()
        
class Related(Base):
    __tablename__ = 'related'
    id = Column(Integer, primary_key=True)

class SomeOther(Base):
    __tablename__ = 'some_other'
    id = Column(Integer, primary_key=True)


engine = create_engine('sqlite://', echo='debug')

Base.metadata.create_all(engine)

session = Session(engine)

rel = Related()
session.add(rel)
session.commit()

p1 = Parent(related_id=rel.id)
dump = pickle.dumps(p1)
load= pickle.loads(dump)
print load.related
assert load.related is rel

session.add(load)
session.commit()

print load.related
assert load.related is rel

dump = pickle.dumps(load)
load2 = pickle.loads(dump)

print load2.related
assert load2.related is not rel # since it was serialized normally

load2 = session.merge(load2)
assert load2.related is rel
-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalch...@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

Reply via email to