[Sqlalchemy-tickets] Issue #3365: evaluator can't locate ORM entity when using a many-to-one compar
Brought to you by:
zzzeek
|
From: Mike B. <iss...@bi...> - 2015-04-14 14:59:21
|
New issue 3365: evaluator can't locate ORM entity when using a many-to-one comparison https://bitbucket.org/zzzeek/sqlalchemy/issue/3365/evaluator-cant-locate-orm-entity-when Mike Bayer: because we don't include the parentmapper annotation in the _optimized_compare(): ``` #!python from sqlalchemy import Column, Integer, Text, ForeignKey, create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import Session, relationship Base = declarative_base() class Parent(Base): __tablename__ = "parent" id = Column(Integer, primary_key=True) class Child(Base): __tablename__ = "child" _id_parent = Column( "id_parent", Integer, ForeignKey(Parent.id), primary_key=True) name = Column(Text, primary_key=True) parent = relationship(Parent) engine = create_engine('sqlite://') Base.metadata.create_all(engine) session = Session(engine) p = Parent(id=1) session.add(p) session.commit() c = Child(name="foo", parent=p) session.add(c) session.commit() session.query(Child).filter(Child.parent == p).delete("evaluate") ``` There are two approaches. One is that we look into getting parentmapper back into PJ/SJ. This was removed a long time ago but we might want to revisit this assumption as mechanics have changed a lot. It would be nice if these annotations were set up at relationship startup time, rather than having to re-inject when we build up relationship comparator operations. The other is probably what we should do here and is just search for the column in the local mapper. We also shouldn't assume ".key" on the clause itself is important, there don't seem to be tests for that case and it should probably raise, so this needs a lot more tests: ``` #!diff diff --git a/lib/sqlalchemy/orm/evaluator.py b/lib/sqlalchemy/orm/evaluator.py index 1e828ff..814b28f 100644 --- a/lib/sqlalchemy/orm/evaluator.py +++ b/lib/sqlalchemy/orm/evaluator.py @@ -7,6 +7,7 @@ import operator from ..sql import operators +from .. import inspect class UnevaluatableError(Exception): @@ -28,6 +29,7 @@ _notimplemented_ops = set(getattr(operators, op) class EvaluatorCompiler(object): def __init__(self, target_cls=None): self.target_cls = target_cls + self.mapper = inspect(target_cls) if target_cls is not None else None def process(self, clause): meth = getattr(self, "visit_%s" % clause.__visit_name__, None) @@ -58,8 +60,12 @@ class EvaluatorCompiler(object): parentmapper.class_ ) key = parentmapper._columntoproperty[clause].key + elif self.mapper and clause in self.mapper._columntoproperty: + key = self.mapper._columntoproperty[clause].key else: - key = clause.key + raise UnevaluatableError( + "Can't evaluate column: %s" % clause + ) get_corresponding_attr = operator.attrgetter(key) return lambda obj: get_corresponding_attr(obj) diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py index b649c9e..7ac0ab5 100644 --- a/lib/sqlalchemy/orm/relationships.py +++ b/lib/sqlalchemy/orm/relationships.py @@ -1372,6 +1372,7 @@ class RelationshipProperty(StrategizedProperty): if adapt_source: criterion = adapt_source(criterion) + return criterion def _lazy_none_clause(self, reverse_direction=False, adapt_source=None): ``` |