detect one-to-many parent moves

Issue #1764 resolved
Former user created an issue

I'm using 0.6beta2r6902 (SVN Trunk), but ran into trouble trying to update a backref relation using a new ORM object. Happens both on sqlite and postgresql. The id on the new object isn't carried forward to the "update" operation.

See the attached test case.

Comments (10)

  1. Mike Bayer repo owner

    your many-to-one delete-orphan is asking the session to do something its not currently capable of. the original A should be deleted and that is not happening here. As a workaround, don't use many-to-one delete-orphan.

  2. Mike Bayer repo owner

    oh more simply, set the first "a.c" to None. the backref is actually failing to remove C from its original A.

  3. Mike Bayer repo owner

    ...and....that's not part of the backref's contract. backrefs only work one level, so it can't keep bouncing back until it gets to the first "A". So there may be no bug here. you need to remove "C" from the first "A" or it gets pulled into the original A's deletion. Will try to think of a way to create a more informative message, if possible.

  4. Former user Account Deleted

    The many-to-one is a red herring. Changing it so that it's one-one it fails the same way, or do you mean something else? This seems extra tricky because of the not-null on C's foreign key combined with the delete-orphan.

  5. Mike Bayer repo owner

    the "many-to-one" is involved only because "delete-orphan" is causing the original A to be deleted. Only the delete of A without de-associating it is needed to illustrate the behavior:

    parent = Table("parent", metadata,
        Column("id", Integer, primary_key=True)
    )
    child = Table("child", metadata,
        Column("id", Integer, primary_key=True),
        Column('parent_id', Integer, ForeignKey('parent.id'), nullable=False)
    )
    
    class Parent(object):
        pass
    class Child(object):
        pass
    p1, p2, c1 = Parent(), Parent(), Child()
    p1.child = c1
    sess.add_all([c1](p1,))
    sess.flush()
    
    sess.delete(p1)
    
    p2.child = c1
    sess.add(p2)
    
    sess.flush()
    

    Above, while relationally speaking c1 can only be associated with one Parent at a time, from an object reference perspective it is associated with both. In the current 0.5/0.6 beta unit of work, the NULL set of c1.parent_id from the delete of p1 happens after the positive set of c1.parent_id from the association to p2.

    Using backrefs, such as if we also had "Child.parent" here, doesn't have any effect on automatically de-associating c1 from p1, if the original "move" event is "p2.child = c1", this because backrefs currently don't function more than one step. That's a different issue but I generally don't like to rely upon backrefs for integrity type stuff.

    As it turns out, I can't get the error to occur using the new unit of work described in #1742. However, I was able to identify this only as an artifact of that version's approach, and its likely that the error could be reproduced there in some cases. So I have added a minimal "parent" detection to that version, in 44c67fef8cc578ffbca409ad95e6471b4cb4d02a , which is not yet merged to the main repo (its http://bitbucket.org/zzzeek/uow_rethink/changeset/44c67fef8cc578ffbca409ad95e6471b4cb4d02a/ at the moment for reference). So the bug is fixed there and we can close this ticket once that branch is merged.

  6. Former user Account Deleted

    It probably doesn't matter much, but in case it does: The foreign key is on the parent in my original test, not on the child, which would raise an error when attempting to create p2 without giving it a child. I think I understand the problem now though.

  7. Log in to comment