cannot add child of two parents to one parent

Issue #1201 resolved
Former user created an issue

If a child has two parents, and one of parent attributes is set on the child, then the child cannot be added to the other parent's child relation list (the parent will not adopt the child).

version is 0.5beta3

The full example hopefully will be attached, but here is the critical part: p = DBSession.query(Parent).filter(Parent.name == u'tmp').one() p2 = DBSession.query(Parent2).filter(Parent2.name == u'tmp').one() # print p.children # uncommenting this prevents the error c = Child(name=u'the child', parent2=p2) p.children.append(c) DBSession.flush()

The p.children.append(c) raises an error that child.parent_id cannot be null (the parent did not set it's foreign key in the child).

Comments (3)

  1. Mike Bayer repo owner

    the session's autoflush is executing upon the lazyload of the p.children access, and you can see this in the stack trace generated.

    Strategies to work around include:

    def add_child():
        p = DBSession.query(Parent).filter(Parent.name == u'tmp').one()
        p2 = DBSession.query(Parent2).filter(Parent2.name == u'tmp').one()
        # print p.children # uncommenting this prevents the error
        DBSession.autoflush = False
        c = Child(name=u'the child', parent2=p2)
        p.children.append(c)
        DBSession.flush()
        DBSession.autoflush = True
    

    a @without_autoflush decorator which does the above also works, such as this one based on Pylons @decorator:

    @decorator
    def without_autoflush(fn, self, *args, **kwargs):
        """Disable the Session autoflush feature
        for the duration of the decorated method.
    
        """
        Session().autoflush=False
        try:
            return fn(self, *args, **kwargs)
        finally:
            Session().autoflush=True
    

    also, you can fully assemble Child before accessing any collections:

        c = Child(name=u'the child', parent=p, parent2=p2)
    

    you can even disable "save-update" cascade from Parent2 to Child to delay adding the Child object:

         parent_id = Column(Integer, ForeignKey("parent.id", ondelete="CASCADE"), nullable=False)
         parent = relation(Parent, backref=backref('children', cascade="none", order_by=id))
    
         parent2_id = Column(Integer, ForeignKey("parent2.id", ondelete="CASCADE"), nullable=False)
         parent2 = relation(Parent2, backref=backref('children', order_by=id, cascade="none"))
    
    ...
    
    def add_child():
        p = DBSession.query(Parent).filter(Parent.name == u'tmp').one()
        p2 = DBSession.query(Parent2).filter(Parent2.name == u'tmp').one()
        # print p.children # uncommenting this prevents the error
        c = Child(name=u'the child', parent2=p2)
        p.children.append(c)
        DBSession.add(c)
        DBSession.flush()
    
  2. Log in to comment