On Sep 6, 2010, at 2:11 PM, Kent Bower wrote: > Also, I was hoping you would tell me whether this would be a candidate for > subclassing InstrumentedAttribute? Would that make more sense or providing > custom __getstate__ & __setstate__ ?
__getstate__ / __setstate__ are pretty much what I like to use for pickle stuff, unless some exotic situation makes me have to use __reduce__. One problem with the recipe is that theres no 'deferred' loading of attributes. So in that sense playing with InstrumentedAttribute would give you a chance to put a callable in there that does what you want. There is also the possibility that __setstate__ can load up callables into the instance_state using state.set_callable(). This is a callable that triggers when you access the attribute that is otherwise None. There's a little bit of fanfare required to get that callable to assign to the attribute in the right way. Attached is an example of that. This is all a little more shaky since the state/callable API isn't really public. Hasn't changed for awhile but there's no guarantee.
> > Thanks for your help, hopefully I'll be able to contribute such a recipe. > > Kent > > > >> >>> >>> Since sqla won't load that for me in the case of transient, I need to load >>> the relation manually (unless you feel like enhancing that as well). >> >> its not an enhancement - it was a broken behavior that was specifically >> removed. The transient object has no session, so therefore no SQL can be >> emitted - there's no context established. >> >> >> >>> >>> Now I can manually emulate the obj being persistent with your changes for >>> >>> On Sep 6, 2010, at 10:58 AM, Michael Bayer <mike...@zzzcomputing.com> wrote: >>> >>>> >>>> On Sep 6, 2010, at 9:06 AM, Kent wrote: >>>> >>>>> with_parent seems to add a join condition. >>>> >>>> OK, so I guess you read the docs which is why you thought it joined and >>>> why you didn't realize it doesn't work for transient. r20b6ce05f194 >>>> changes all that so that with_parent() accepts transient objects and will >>>> do the "look at the attributes" thing. The docs are updated as this >>>> method does use the lazy loader SQL mechanism, not a join. >>>> >>>> >>>> >>>>> Is there a way to get at >>>>> the query object that would be rendered from a lazy load (or what >>>>> "subqueryload" would render on the subsequent load), but on a >>>>> transient object, if i supply the session? >>>>> >>>>> even though not "recommended", can it make sqla believe my transient >>>>> object is detached by setting its state key? >>>>> >>>>> There are reasons i do not want to add this to the session and >>>>> disabling autoflush would also cause problems. >>>>> >>>>> >>>>> >>>>> On Sep 3, 9:58 am, Michael Bayer <mike...@zzzcomputing.com> wrote: >>>>>> On Sep 3, 2010, at 9:36 AM, Kent wrote: >>>>>> >>>>>>> For the case of customerid = '7', that is a simple problem, but when >>>>>>> it is a more complex join condition, we only wanted to define this >>>>>>> condition in one single place in our application (namely, the orm). >>>>>>> That way, if or when that changes, developers don't need to search for >>>>>>> other places in the app that needed to manually duplicate the logic of >>>>>>> the orm join condition. >>>>>> >>>>>>> If I supplied the DBSession to sqla, it would know how to create the >>>>>>> proper Query object for this lazyload. Can you point me in the right >>>>>>> direction (even if where you point me is not currently part of the >>>>>>> public API)? >>>>>> >>>>>> Query has the with_parent() method for this use case. >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>>> Thanks again, >>>>>>> Kent >>>>>> >>>>>>> -- >>>>>>> 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 >>>>>>> athttp://groups.google.com/group/sqlalchemy?hl=en. >>>>> >>>>> -- >>>>> 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. >>>>> >>>> >>>> -- >>>> 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. >>>> >>> >>> -- >>> 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. >>> >> >> -- >> 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. >> > > -- > 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. >
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 from sqlalchemy.orm.attributes import instance_state, ATTR_WAS_SET 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 def deferred_transient_callable(prop, crit, state, dict_): def go(**kw): result = session.query(prop.mapper).filter(crit).one() # pop out the callable... state.reset(dict_, prop.key) # populate the result via dict. this produces no # net change during flush, relying upon the FK dict_[prop.key] = result # or, populate the result with a "change" event. # the UOW picks it up during flush. # state.get_impl(prop.key).set(state, dict_, result, None) # tell the InstrumentedAttribute "its handled" return ATTR_WAS_SET return go 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): state = instance_state(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, ) state.set_callable(self.__dict__, prop.key, deferred_transient_callable( prop, crit, state, self.__dict__ )) 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) # not present assert 'related' not in load.__dict__ # load print load.related # present ! assert 'related' in load.__dict__ 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.