I'm writing a GUI app that uses SA sessions extensively to save/update/delete 
graphs of persistent objects. The session usually does a pretty good job of 
figuring out what SQL needs to be executed to persist the entire object graph 
on session.flush(). However, I'm having problems with creating and then 
deleting the same object (and its children) within a session. Here's a short 
example that demonstrates the problem:


01  mapper(Address, addresses)
02  mapper(User, users, properties=dict(
03    addresses=relation(Address,
04      cascade="all,delete-orphan",
05      backref="user"
06    )
07  ))
08  s = create_session()
09 10 u = User()
11  s.save(u)
12 13 a = Address()
14  u.addresses.append(a)
15 16 # later before session is flushed
17  u.addresses.remove(a)
18 19 # and even later the user is deleted
20  s.delete(u)
21 22 s.flush() # finally persist pending changes
23  # this (erroneously) causes "a" to be persisted
23  # even though it was never saved in the session


This is a minimal example to demonstrate the problem. In actuality the end-user will be adding lots 
of new Users and Addresses (fictional entities used for this example only) some of which will be 
saved and others will be "deleted" before they ever get the chance to become persistent. 
The problem I am having is a side affect of the fact that the new Address (a) is registered as 
"new" in the session when it is appended to u.addresses (line 14), but it is not 
unregistered when it is removed from u.addresses (line 17).

I think that's a bug. A test case is also attached.

~ Daniel
from testbase import AssertMixin
import testbase
import unittest, sys, datetime

import tables
from tables import *

db = testbase.db
from sqlalchemy import *


class SessionTest(AssertMixin):
    
    def setUpAll(self):
        db.echo = False
        tables.create()
        tables.data()
        db.echo = testbase.echo
    def tearDownAll(self):
        db.echo = False
        tables.drop()
        db.echo = testbase.echo
    def tearDown(self):
        clear_mappers()
    def setUp(self):
        pass
        
    def test_delete_new_object(self):
        mapper(Address, addresses)
        mapper(User, users, properties=dict(
            addresses=relation(Address, cascade="all,delete-orphan", 
backref="user")
        ))
        s = create_session()

        u = User()
        s.save(u)
        a = Address()
        assert a not in s.new
        u.addresses.append(a)
        #assert a not in s.new # fails
        u.addresses.remove(a)
        #assert a not in s.new # fails
        s.delete(u)
        s.flush() # (erroneously) causes "a" to be persisted
        assert u.user_id is None, "Error: user should not be persistent"
        assert a.address_id is None, "Error: address should not be persistent"

        """
        Suggested behavior:
        instead of immediately registering objects that are added to a 
collection
        as "new", the registration should be deferred until the session is 
flushed.
        On session.flush() any object "u" with a non-persistent child "a" and a
        cascading save rule will cause "a" to be persisted at that time.
        """

        
if __name__ == "__main__":    
    testbase.main()
-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Sqlalchemy-users mailing list
Sqlalchemy-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sqlalchemy-users

Reply via email to