]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- add __table_cls__ option to declarative, not publicized yet, is for the moment
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 12 Mar 2012 20:14:14 +0000 (13:14 -0700)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 12 Mar 2012 20:14:14 +0000 (13:14 -0700)
for the benefit of the test.lib.schema package.
- use test.lib.schema.Table for the table within test.lib.fixtures.DeclarativeMappedTest
- [bug] Removed the check for number of
rows affected when doing a multi-delete
against mapped objects.   If an ON DELETE
CASCADE exists between two rows, we can't
get an accurate rowcount from the DBAPI;
this particular count is not supported
on most DBAPIs in any case, MySQLdb
is the notable case where it is.
[ticket:2403]

CHANGES
lib/sqlalchemy/ext/declarative.py
lib/sqlalchemy/orm/persistence.py
test/lib/fixtures.py
test/orm/test_unitofwork.py

diff --git a/CHANGES b/CHANGES
index cc5f66348457440da58e4265ad08a3f094c6bbb8..3936964c4c9ac17c5eeb6b2f9ee23140ebb2fdfd 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -21,6 +21,16 @@ CHANGES
     invokes common table expression support
     from the Core (see below). [ticket:1859]
 
+  - [bug] Removed the check for number of
+    rows affected when doing a multi-delete
+    against mapped objects.   If an ON DELETE
+    CASCADE exists between two rows, we can't
+    get an accurate rowcount from the DBAPI;
+    this particular count is not supported
+    on most DBAPIs in any case, MySQLdb
+    is the notable case where it is.
+    [ticket:2403]
+
   - [bug] Fixed bug whereby objects using
     attribute_mapped_collection or 
     column_mapped_collection could not be
index 891130a48d5e6505b12b6b635fb52d40862d0ce5..faf575da1cee88c2fe2bbb4f94ab46721386a516 100755 (executable)
@@ -1213,6 +1213,12 @@ def _as_declarative(cls, classname, dict_):
                 del our_stuff[key]
     cols = sorted(cols, key=lambda c:c._creation_order)
     table = None
+
+    if hasattr(cls, '__table_cls__'):
+        table_cls = util.unbound_method_to_callable(cls.__table_cls__)
+    else:
+        table_cls = Table
+
     if '__table__' not in dict_:
         if tablename is not None:
 
@@ -1230,7 +1236,7 @@ def _as_declarative(cls, classname, dict_):
             if autoload:
                 table_kw['autoload'] = True
 
-            cls.__table__ = table = Table(tablename, cls.metadata,
+            cls.__table__ = table = table_cls(tablename, cls.metadata,
                                           *(tuple(cols) + tuple(args)),
                                            **table_kw)
     else:
index 31c9891b82907051d387b6e00eb4613d382eb678..55b9bf84a1d4eadcaf24cbb4d165fe183612e9e7 100644 (file)
@@ -642,12 +642,10 @@ def _emit_delete_statements(base_mapper, uowtransaction, cached_connections,
 
     for connection, del_objects in delete.iteritems():
         statement = base_mapper._memo(('delete', table), delete_stmt)
-        rows = -1
 
         connection = cached_connections[connection]
 
-        if need_version_id and \
-                not connection.dialect.supports_sane_multi_rowcount:
+        if need_version_id:
             # TODO: need test coverage for this [ticket:1761]
             if connection.dialect.supports_sane_rowcount:
                 rows = 0
@@ -656,6 +654,12 @@ def _emit_delete_statements(base_mapper, uowtransaction, cached_connections,
                 for params in del_objects:
                     c = connection.execute(statement, params)
                     rows += c.rowcount
+                if rows != len(del_objects):
+                    raise orm_exc.StaleDataError(
+                        "DELETE statement on table '%s' expected to "
+                        "delete %d row(s); %d were matched." % 
+                        (table.description, len(del_objects), c.rowcount)
+                    )
             else:
                 util.warn(
                     "Dialect %s does not support deleted rowcount "
@@ -664,16 +668,8 @@ def _emit_delete_statements(base_mapper, uowtransaction, cached_connections,
                     stacklevel=12)
                 connection.execute(statement, del_objects)
         else:
-            c = connection.execute(statement, del_objects)
-            if connection.dialect.supports_sane_multi_rowcount:
-                rows = c.rowcount
+            connection.execute(statement, del_objects)
 
-        if rows != -1 and rows != len(del_objects):
-            raise orm_exc.StaleDataError(
-                "DELETE statement on table '%s' expected to "
-                "delete %d row(s); %d were matched." % 
-                (table.description, len(del_objects), c.rowcount)
-            )
 
 def _finalize_insert_update_commands(base_mapper, uowtransaction, 
                             states_to_insert, states_to_update):
index 03116fbc113356fbd16c051a8652c49600aac2ca..41a72c9a49906ebf27f8f212c8149a46a13e60f7 100644 (file)
@@ -1,4 +1,5 @@
 from test.lib import testing
+from test.lib import schema
 from test.lib.testing import adict
 from test.lib.engines import drop_all_tables
 import sys
@@ -325,10 +326,11 @@ class DeclarativeMappedTest(MappedTest):
                 cls_registry[classname] = cls
                 return DeclarativeMeta.__init__(
                         cls, classname, bases, dict_)
+        class DeclarativeBasic(object):
+            __table_cls__ = schema.Table
         _DeclBase = declarative_base(metadata=cls.declarative_meta, 
-                            metaclass=FindFixtureDeclarative)
-        class DeclarativeBasic(BasicEntity):
-            pass
+                            metaclass=FindFixtureDeclarative,
+                            cls=DeclarativeBasic)
         cls.DeclarativeBasic = _DeclBase
         fn()
         if cls.declarative_meta.tables:
index b0dbfe3907db8e4ea8c81eac7bd42fe54e4698c9..baf7754b3e22605bf15c5d073bcf0cbaba05b509 100644 (file)
@@ -567,6 +567,31 @@ class PassiveDeletesTest(fixtures.MappedTest):
         mapper(MyClass, mytable)
         assert_raises(sa.exc.SAWarning, sa.orm.configure_mappers)
 
+class BatchDeleteIgnoresRowcountTest(fixtures.DeclarativeMappedTest):
+    __requires__ = ('foreign_keys',)
+    @classmethod
+    def setup_classes(cls):
+        class A(cls.DeclarativeBasic):
+            __tablename__ = 'A'
+            __table_args__ = dict(test_needs_fk=True)
+            id = Column(Integer, primary_key=True)
+            parent_id = Column(Integer, ForeignKey('A.id', ondelete='CASCADE'))
+
+    def test_delete_both(self):
+        A = self.classes.A
+        session = Session(testing.db)
+
+        a1, a2 = A(id=1),A(id=2, parent_id=1)
+
+        session.add_all([a1, a2])
+        session.flush()
+
+        session.delete(a1)
+        session.delete(a2)
+
+        # no issue with multi-row count here
+        session.flush()
+
 class ExtraPassiveDeletesTest(fixtures.MappedTest):
     __requires__ = ('foreign_keys',)