From: Mike Bayer Date: Fri, 31 Mar 2006 23:43:20 +0000 (+0000) Subject: added unit test for the old commit that was in [changeset:1186]. modified its behavi... X-Git-Tag: rel_0_1_6~42 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e2198009080028d9e4bbce840bb5bc7536910a6e;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git added unit test for the old commit that was in [changeset:1186]. modified its behavior a bit to not delete private relationships unless they were already marked as deleted at the attribute manipulation level. got "switching" behavior from one private relationship to another to work, added a unit test for that. --- diff --git a/lib/sqlalchemy/mapping/properties.py b/lib/sqlalchemy/mapping/properties.py index fa14dc4deb..6d30c5ffe8 100644 --- a/lib/sqlalchemy/mapping/properties.py +++ b/lib/sqlalchemy/mapping/properties.py @@ -451,7 +451,10 @@ class PropertyLoader(MapperProperty): for child in childlist.deleted_items() + childlist.unchanged_items(): if child is None: continue - uowcommit.register_object(child, isdelete=True) + # if private child object, and is in the uow's "deleted" list, + # insure its in the list of items to be deleted + if child in uowcommit.uow.deleted: + uowcommit.register_object(child, isdelete=True) elif self.post_update: # post_update means we have to update our row to not reference the child object # before we can DELETE the row @@ -462,16 +465,15 @@ class PropertyLoader(MapperProperty): # head object is being deleted, and we manage its list of child objects # the child objects have to have their foreign key to the parent set to NULL if self.private and not self.post_update: - # if we are privately managed, then all our objects should - # have been marked as "todelete" already and no attribute adjustment is needed. - # however, if they say objectstore.commit(x), i.e. on an individual object, - # then this extra step is more important. for obj in deplist: childlist = getlist(obj, False) for child in childlist.deleted_items() + childlist.unchanged_items(): if child is None: continue - uowcommit.register_object(child, isdelete=True) + # if private child object, and is in the uow's "deleted" list, + # insure its in the list of items to be deleted + if child in uowcommit.uow.deleted: + uowcommit.register_object(child, isdelete=True) else: for obj in deplist: childlist = getlist(obj, False) @@ -529,8 +531,6 @@ class PropertyLoader(MapperProperty): for child in childlist.deleted_items(): if not self.private: self._synchronize(obj, child, None, True) - if self.direction == PropertyLoader.ONETOMANY: - # for a cyclical task, this registration is handled by the objectstore uowcommit.register_object(child, isdelete=self.private) def execute(self, instance, row, identitykey, imap, isnew): diff --git a/lib/sqlalchemy/mapping/unitofwork.py b/lib/sqlalchemy/mapping/unitofwork.py index 1eea546df7..7c3ca8fd70 100644 --- a/lib/sqlalchemy/mapping/unitofwork.py +++ b/lib/sqlalchemy/mapping/unitofwork.py @@ -44,7 +44,8 @@ class UOWListElement(attributes.ListElement): def list_value_changed(self, obj, key, item, listval, isdelete): sess = get_session(obj) if not isdelete and sess.deleted.contains(item): - raise InvalidRequestError("re-inserting a deleted value into a list") + #raise InvalidRequestError("re-inserting a deleted value into a list") + del sess.deleted[item] sess.modified_lists.append(self) if self.deleteremoved and isdelete: sess.register_deleted(item) diff --git a/test/objectstore.py b/test/objectstore.py index 73bbc1da43..44945dcbd5 100644 --- a/test/objectstore.py +++ b/test/objectstore.py @@ -263,6 +263,79 @@ class PKTest(AssertMixin): e.assigned = datetime.date.today() e.data = 'some more data' objectstore.commit() + +class PrivateAttrTest(AssertMixin): + """tests various things to do with private=True mappers""" + def setUpAll(self): + global a_table, b_table + a_table = Table('a',testbase.db, + Column('a_id', Integer, Sequence('next_a_id'), primary_key=True), + Column('data', String(10)), + ).create() + + b_table = Table('b',testbase.db, + Column('b_id',Integer,Sequence('next_b_id'),primary_key=True), + Column('a_id',Integer,ForeignKey('a.a_id')), + Column('data',String(10))).create() + def tearDownAll(self): + b_table.drop() + a_table.drop() + def setUp(self): + objectstore.clear() + clear_mappers() + + def testsinglecommit(self): + """tests that a commit of a single object deletes private relationships""" + class A(object):pass + class B(object):pass + + assign_mapper(B,b_table) + assign_mapper(A,a_table,properties= {'bs' : relation + (B.mapper,private=True)}) + + # create some objects + a = A(data='a1') + a.bs = [] + + # add a 'B' instance + b1 = B(data='1111') + a.bs.append(b1) + + # add another one + b2 = B(data='2222') + a.bs.append(b2) + + # inserts both A and Bs + objectstore.commit(a) + + objectstore.delete(a) + objectstore.commit(a) + + assert b_table.count().scalar() == 0 + + def testswitchparent(self): + """tests that you can switch the parent of an object in a backref scenario""" + class A(object):pass + class B(object):pass + + assign_mapper(B,b_table) + assign_mapper(A,a_table,properties= { + 'bs' : relation (B.mapper,private=True, backref='a')} + ) + a1 = A(data='testa1') + a2 = A(data='testa2') + b = B(data='testb') + b.a = a1 + objectstore.commit() + objectstore.clear() + sess = objectstore.get_session() + a1 = A.mapper.get(a1.a_id) + a2 = A.mapper.get(a2.a_id) + assert a1.bs[0].a is a1 + b = a1.bs[0] + b.a = a2 + assert b not in sess.deleted + objectstore.commit() class DefaultTest(AssertMixin): """tests that when saving objects whose table contains DefaultGenerators, either python-side, preexec or database-side,