On Jul 25, 2013, at 2:59 PM, Edouard Sioufi <[email protected]> wrote:

> 
> If the parent attribute of a child is changed from parent1 to parent2, not 
> only is the child added to the children of the new parent parent2, it is also 
> removed from the collection of the old parent parent1. Example:
> 
> child.parent = parent1
> print parent1
> print parent2
> print child
> print '================================='
> child.parent = parent2
> print parent1
> print parent2
> print child
> 
>  yields the following output:
> 
> >>> <Parent('Parent 1','[<Child('Child 1','Parent 1')>]')>
> >>> <Parent('Parent 2','[]')>
> >>> <Child('Child 1','Parent 1')>
> >>> =================================
> >>> <Parent('Parent 1','[]')>
> >>> <Parent('Parent 2','[<Child('Child 1','Parent 2')>]')>
> >>> <Child('Child 1','Parent 2')>

in this case, associating child.parent = parent2 causes an event to occur which 
receives "parent2" as the "new" value of child.parent and also is aware of 
"parent1" being the "old" value.  This event is processed by the backref setup, 
such that parent2.children now receives "child" in its collection, and such 
that "child" is removed from parent1.children as a result of parent1 being 
de-associated from child.parent.    These two events fire off as siblings to 
each other.


> 
> However, if the child is appended to group2.children when it has group1 as a 
> parent, the child is added to the children collection of group2 but not 
> removed from the children collection of group1. Example:
> 
> child.parent = parent1
> print parent1
> print parent2
> print child
> print '================================='
> parent2.children.append(child)
> print parent1
> print parent2
> print child
> 
> The following output shows the issue:
> <Parent('Parent 1','[<Child('Child 1','Parent 1')>]')>
> <Parent('Parent 2','[]')>
> <Child('Child 1','Parent 1')>
> =================================
> <Parent('Parent 1','[<Child('Child 1','Parent 2')>]')>
> <Parent('Parent 2','[<Child('Child 1','Parent 2')>]')>
> <Child('Child 1','Parent 2')>

in this case, appending child to parent2.children causes an event to occur 
which receives "child" as a new member of parent2.children.   This event is 
processed by the backref setup such that child.parent is now assigned to 
"parent2", replacing "parent1".    The event does *not* continue to propagate 
along additional backref lines - in the case of the positive reference 
(child.parent becoming parent2), this would produce an endless loop, if 
parent2.children fired off child.parent which then fired off parent2.children 
which continued indefinitely.   In the case of the negative reference 
(child.parent no longer being parent1), the event propagation also stops once 
we're already in a backref event; while it may be possible to test that the old 
collection is present in memory, the mechanics here are not quite that 
straightforward as sometimes the old collection isn't present and such and 
there are still lots of endless loop cases that come up (I just tried in tests 
and many fail if I remove this check).   The recursion checks in this area are 
fairly aggressive as there are a lot of edge cases that can occur, sometimes 
due to user error, where we can't efficiently determine at what point we need 
to stop backreffing.   I have a little bit of time at the moment so I can look 
to see if in the current codebase there might be some refinement that can be 
easily made.

This type of situation is typically not a long lasting issue because once the 
Session is committed, collections are expired and are refreshed.

> 
> I am quite new to SQLAlchemy and I don't know if this is expected behavior.

it currently is.


> I nevertheless dare to say that I find the states of the objects in memory to 
> be incoherent as child1 no longer has parent1 as its parent while parent1 
> still thinks that it has child1 as a child.

the backref mechanics are a convenience feature that synchronize the state of 
collections in memory to a limited degree, without needing to access the 
database again in order to get the fully correct result.

> Moreover, it is not clear what happens when the objects are persisted to the 
> database (what would the value of child1.parentId be?)

Persistence works based on change events, so above the attribute and collection 
assignments result in the correct UPDATE statements.  The actual state of the 
collections/attributes is not actually that important compared to the reception 
of the change event.


-- 
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 [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to