[Sqlalchemy-tickets] Issue #4128: common mapperoption recipes broken since 1.0 (zzzeek/sqlalchemy)
Brought to you by:
zzzeek
From: Michael B. <iss...@bi...> - 2017-11-01 13:54:44
|
New issue 4128: common mapperoption recipes broken since 1.0 https://bitbucket.org/zzzeek/sqlalchemy/issues/4128/common-mapperoption-recipes-broken-since Michael Bayer: ``` #!python from sqlalchemy import * from sqlalchemy.orm import * from sqlalchemy.ext.declarative import declarative_base import datetime from sqlalchemy.orm.interfaces import MapperOption e = create_engine('sqlite://', echo=True) Base = declarative_base(e) class Parent(Base): __tablename__ = 'parent' id = Column(Integer, primary_key=True) timestamp = Column(TIMESTAMP, nullable=False) children = relation("Child") class Child(Base): __tablename__ = 'child' id = Column(Integer, primary_key=True) parent_id = Column(Integer, ForeignKey('parent.id'), nullable=False) timestamp = Column(TIMESTAMP, nullable=False) Base.metadata.create_all() Parent.temporal_children = relation( Child, primaryjoin=and_( Parent.id == Child.parent_id, Child.timestamp.between( bindparam("temporal_lower"), bindparam("temporal_upper") ) ), viewonly=True ) session = sessionmaker()() c1, c2, c3, c4, c5 = [ Child(timestamp=datetime.datetime(2009, 10, 15, 12, 00, 00)), Child(timestamp=datetime.datetime(2009, 10, 17, 12, 00, 00)), Child(timestamp=datetime.datetime(2009, 10, 20, 12, 00, 00)), Child(timestamp=datetime.datetime(2009, 10, 12, 12, 00, 00)), Child(timestamp=datetime.datetime(2009, 10, 17, 12, 00, 00)), ] p1, p2 = [ Parent( timestamp=datetime.datetime(2009, 10, 15, 12, 00, 00), children=[c1, c2, c3] ), Parent( timestamp=datetime.datetime(2009, 10, 17, 12, 00, 00), children=[c4, c5] )] session.add_all([p1, p2]) session.commit() class TemporalOption(MapperOption): propagate_to_loaders = True def __init__(self, range_lower, range_upper): self.range_lower = range_lower self.range_upper = range_upper def process_query_conditionally(self, query): """process query during a lazyload""" query._params = query._params.union( dict( temporal_lower=self.range_lower, temporal_upper=self.range_upper )) def process_query(self, query): """process query during a primary user query""" # apply bindparam values self.process_query_conditionally(query) # requires a query against a single mapper parent_cls = query._mapper_zero().class_ filter_crit = parent_cls.timestamp.between( bindparam("temporal_lower"), bindparam("temporal_upper") ) if query._criterion is None: query._criterion = filter_crit else: query._criterion = query._criterion & filter_crit session.expire_all() # for a clean test # ISSUE 1: if we don't do populate_existing() here, the load_options # are not applied to Parent because as of 1.0 we no longer # apply load_options unconditionally, only on "new" or "populate existing" parents = session.query(Parent).\ options( TemporalOption( datetime.datetime(2009, 10, 16, 12, 00, 00), datetime.datetime(2009, 10, 18, 12, 00, 00)) ).all() assert parents[0] == p2 # ISSUE 2: _emit_lazyload() now calls: # lazy_clause, params = self._generate_lazy_clause( # state, passive=passive) # producing params separate from the binds, # due to e3b46bd62405b6ff57119e164718118f3e3565e0 # issue #3054, introduction of baked query. params set up by the # mapper option must be honored assert parents[0].temporal_children == [c5] ``` |