topological sorting issue with self-ref m2o

Issue #2477 resolved
Mike Bayer repo owner created an issue
from sqlalchemy.ext.declarative import declarative_base, declared_attr
from sqlalchemy import *
from sqlalchemy.orm import relationship, Session, backref

Base = declarative_base()

class Group(Base):
    __tablename__ = 'group'
    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey('group.id'))
    name = Column(Unicode, nullable=False)
    children = relationship("Group",
                            backref=backref("parent", 
                                    # fixes
                                    #active_history=True,
                                    remote_side="Group.id"))

    def __init__(self, name):
        self.name = name

e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)
s = Session(e)

g1 = Group(name="g1")
g1.parent = Group(name='g2')
s.add(g1)
s.commit()
s.close()

g1 = s.query(Group).filter_by(name='g1').one()
g1.parent = None
s.commit()

assert g1.parent is None

without active history, g1.parent gets PASSIVE_NO_RESULT in committed state. When the cycle resolution for the self-referential .parent gets figured out, ScalarObjectAttributeImpl.get_all_pending skips entries that are PASSIVE_NO_RESULT.

diff -r 6de2aa03bdfd3fd40b36bc4d052f6f1f10f10fe8 lib/sqlalchemy/orm/attributes.py
--- a/lib/sqlalchemy/orm/attributes.py  Fri Apr 27 15:21:02 2012 -0700
+++ b/lib/sqlalchemy/orm/attributes.py  Wed May 02 14:56:05 2012 -0400
@@ -657,7 +657,9 @@

             if self.key in state.committed_state:
                 original = state.committed_state[self.key](self.key)
-                if original not in (NEVER_SET, PASSIVE_NO_RESULT, None) and \
+                if original is PASSIVE_NO_RESULT:
+                    ret.append((original, original))
+                elif original not in (NEVER_SET, None) and \
                     original is not current:

                     ret.append((instance_state(original), original))

not sure if this is for 0.7.7 or not, as this has been broken like this since at least the new uow in 0.6. need to determine potential backwards incompatibilities.

Comments (4)

  1. Mike Bayer reporter

    another way to do it:

    diff -r 6de2aa03bdfd3fd40b36bc4d052f6f1f10f10fe8 lib/sqlalchemy/orm/attributes.py
    --- a/lib/sqlalchemy/orm/attributes.py  Fri Apr 27 15:21:02 2012 -0700
    +++ b/lib/sqlalchemy/orm/attributes.py  Wed May 02 15:00:14 2012 -0400
    @@ -653,7 +653,7 @@
                 if current is not None:
                     ret = [current)]((instance_state(current),)
                 else:
    -                ret = [               ret = [(None, None)](]
    +)
    
                 if self.key in state.committed_state:
                     original = state.committed_state[self.key](self.key)
    
  2. Log in to comment