Mike, Thank you very much for your quick reply and for creating the test case. Upon closer inspection, I noticed that the errors in my system resulted from a special case of a self-referential relationship as in the following modification of your test case, in which additionally the post_update flag is required. In that case, the assertion fails. However, with an additional remote annotation to make the _del==0 condition unambiguous, the example works again. Now the only question that remains is why the original version was working fine up to 0.9.3.
from sqlalchemy import * from sqlalchemy.orm import * from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() *class C(Base): __tablename__ = 'c' id = Column(Integer, primary_key=True) parent_id = Column(ForeignKey('c.id <http://c.id>')) _del = Column("del", Integer, default=0)* def make_rel(cls, remoteCls, foreignKey, backref_name): br = backref( backref_name, collection_class=list, primaryjoin=and_( remoteCls.id == remote(getattr(cls, foreignKey)), cls._del == 0* # works with: remote(cls._del) == 0* ) ) rel = relationship( remoteCls, remote_side=remoteCls.id, primaryjoin=getattr(cls, foreignKey) == remoteCls.id, *post_update = True*, backref=br) return rel *C.parent = make_rel(C, C, "parent_id", "children")* e = create_engine("sqlite://", echo=True) Base.metadata.create_all(e) s = Session(e) *c1, c2, c3 = C(), C(), C()c0 = C(children=[c1, c2, c3])* s.add(c0) s.commit() *c2._del = 1*s.commit() *assert c0.children == [c1, c3]* On Wed, Mar 16, 2016 at 12:42 PM, Mike Bayer <clas...@zzzcomputing.com> wrote: > > > On 03/16/2016 02:37 PM, Thorsten von Stein wrote: > >> For several years, I have been using a pattern for making a many-to-one >> relationship from *cls* to *remoteCls* with a one-to-many backref with a >> join condition cls.foreignKey == remoteCls.id, where >> *cls* has a deletion flag _del which should exclude *cls* instances with >> >> del != 0 from the backref collection. >> >> Since the condition involving _del is only relevant in the one-to-many >> direction, I defined separate primaryjoin conditions which included this >> condition only for the backref. >> >> br = backref( >> backref, >> collection_class=list, >> primaryjoin=and_(remoteCls.id==remote(getattr(cls, foreignKey)), >> cls._del==0)) >> >> rel = relationship( >> remoteCls, >> remote_side=remoteCls.id, >> primaryjoin=getattr(cls, foreignKey)==remoteCls.id, >> backref=br) >> >> I have used this pattern successfully for years until I recently >> upgraded SqlAlchemy to the latest version and found that the join >> condition on the backref seems to be ignored and queries include >> instances that are flagged as deleted via the _del column. I tested >> several intermediate SqlAlchemy version and found that the first one >> which breaks the pattern is 0.9.4. >> >> Subsequently I found that removing the primary join condition on the >> backref and including the _del != 0 condition in the forward primary >> join condition seems to restore the intended behavior, but now many >> queries involving large collections are dramatically slowed to make this >> solution unworkable. >> >> I reviewed the desciptions of changes, but they did not clarify for me >> why the pattern above does not work any more. Is there a flaw in my code >> that I am missing? >> > > There are no changes that should affect the behavior of relationship in > this way. If anything, I'd wonder if the "0" value here is actually a > boolean and is interacting with some backend-specific typing behavior, but > there's not enough detail here to know. > > Below is a complete test of your concept which succeeds. Please alter > this test appropriately to illustrate your failure condition occurring, > thanks! > > from sqlalchemy import * > from sqlalchemy.orm import * > from sqlalchemy.ext.declarative import declarative_base > > Base = declarative_base() > > > class A(Base): > __tablename__ = 'a' > id = Column(Integer, primary_key=True) > > > class B(Base): > __tablename__ = 'b' > id = Column(Integer, primary_key=True) > a_id = Column(ForeignKey('a.id')) > _del = Column("del", Integer, default=0) > > > def make_rel(cls, remoteCls, foreignKey, backref_name): > br = backref( > backref_name, > collection_class=list, > primaryjoin=and_( > remoteCls.id == remote(getattr(cls, foreignKey)), > cls._del == 0) > ) > > rel = relationship( > remoteCls, > remote_side=remoteCls.id, > primaryjoin=getattr(cls, foreignKey) == remoteCls.id, > backref=br) > return rel > > B.a = make_rel(B, A, "a_id", "bs") > > e = create_engine("sqlite://", echo=True) > Base.metadata.create_all(e) > > s = Session(e) > > b1, b2, b3 = B(), B(), B() > a1 = A(bs=[b1, b2, b3]) > > s.add(a1) > s.commit() > > b2._del = 1 > s.commit() > > assert a1.bs == [b1, b3] > > > > > > > >> -- >> You received this message because you are subscribed to the Google >> Groups "sqlalchemy" group. >> To unsubscribe from this group and stop receiving emails from it, send >> an email to sqlalchemy+unsubscr...@googlegroups.com >> <mailto:sqlalchemy+unsubscr...@googlegroups.com>. >> To post to this group, send email to sqlalchemy@googlegroups.com >> <mailto:sqlalchemy@googlegroups.com>. >> Visit this group at https://groups.google.com/group/sqlalchemy. >> For more options, visit https://groups.google.com/d/optout. >> > > -- > You received this message because you are subscribed to a topic in the > Google Groups "sqlalchemy" group. > To unsubscribe from this topic, visit > https://groups.google.com/d/topic/sqlalchemy/43rA8XsVuBQ/unsubscribe. > To unsubscribe from this group and all its topics, send an email to > sqlalchemy+unsubscr...@googlegroups.com. > To post to this group, send email to sqlalchemy@googlegroups.com. > Visit this group at https://groups.google.com/group/sqlalchemy. > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. Visit this group at https://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.