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 [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/sqlalchemy?hl=en.