[Sorry if this makes it onto the list twice.  I posted through
Gmane NNTP and was told the message was sent to moderation.  I haven't
seen it appear on the list yet.]

        Greetings.  I've got a database with a parent/children
relationship (one-to-many, to clarify).  When a parent is deleted, all
children should be deleted; this is reflected in the schema (ON DELETE
CASCADE) as well as in the SA relationship (private=True).

         However, I'm running into problems in the case where a
child's parent needs to be changed.  I've demonstrated this in the
test script included below.  As near as I can tell the reassignment
works like:

(1) Delete child from old parent.
        (a) Parent says "my children are private, so delete the
            child."
(2) Add child to new parent, resulting in an exception because the
    child has been deleted.

It looks to me like this trouble may originate around
GenericBackrefExtension.set(), where it does a "remove" followed by an
"append."  I tried to step through this code but I wasn't able to
follow it.

       Would it be possible to modify this code (or modify elsewhere)
so that the calls to remove the object from the parent know that the
child is not being removed, but reassigned to a different parent?
I.e., ignore the private=True for cases of reassigning a parent?

      Or, alternatively, will anyone kindly suggest a recipe for
getting around this problem?  The only way I've found is something
roughly like (using variables from below test):

child.foo_id = new_parent.id
child.commit()             # Actually issues SQL UPDATE statement
objectstore.refresh(child) # If you don't refresh, child.foo is None
assert child.new is new_parent

       Thanks for any assistance, and thanks for SA.

Dale

----

from sqlalchemy import *

# -- PostgreSQL 8.1.3, using the following schema:
#
# CREATE TABLE foo (id INTEGER PRIMARY KEY);
# CREATE TABLE bar (id INTEGER PRIMARY KEY,
#                   foo_id INTEGER REFERENCES foo (id) NOT NULL
#                                  ON DELETE CASCADE);
# INSERT INTO foo VALUES (1);
# INSERT INTO foo VALUES (2);
# INSERT INTO bar VALUES (3, 1);

db_uri = "postgres://database=oit_pwdb&user=oit_pwdb&password=qipiY24P"
engine = create_engine(db_uri, echo=True)

foos = Table("foo", engine, autoload=True)
bars = Table("bar", engine, autoload=True)

class Foo (object): pass
assign_mapper(Foo, foos)
class Bar (object): pass
assign_mapper(Bar, bars)

cascade_mappers(Foo, Bar)
Foo.mapper.add_property("bars", relation(Bar, backref="foo", private=True))

# Actual problem section is here.
child = Bar.get(3)
old_parent = Foo.get(1)
assert child.foo is old_parent, "test setup failed" 
new_parent = Foo.get(2)
child.foo = new_parent
# The above statement raises the following exception:
#
#   File
#   
".../python2.4/site-packages/SQLAlchemy-0.1.5-py2.4.egg/sqlalchemy/mapping/unitofwork.py",
#   line 47, in list_value_changed
#     raise InvalidRequestError("re-inserting a deleted value into a list")

# Never get here.
child.commit()
assert child.foo_id == new_parent.id, "parent change failed"


-------------------------------------------------------
This SF.Net email is sponsored by xPML, a groundbreaking scripting language
that extends applications into web and mobile media. Attend the live webcast
and join the prime developer group breaking into this new coding territory!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642
_______________________________________________
Sqlalchemy-users mailing list
Sqlalchemy-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sqlalchemy-users

Reply via email to