summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2013-11-07 20:35:24 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2013-11-07 20:35:24 -0500
commit29a07fc09a6c11b3e05457e2a8122682a90a4419 (patch)
tree166d9127543246e19dd1b471ad7df54e66450993
parent5d0e84434f116460d64d3078f650d9c89004f2a9 (diff)
downloadsqlalchemy-29a07fc09a6c11b3e05457e2a8122682a90a4419.tar.gz
- Fixed bug which prevented history_meta recipe from working with
joined inheritance schemes more than one level deep. - don't need to use _lib.py here anymore now that these features are in sqlalchemy.testing
-rw-r--r--doc/build/changelog/changelog_09.rst6
-rw-r--r--examples/versioning/_lib.py96
-rw-r--r--examples/versioning/history_meta.py3
-rw-r--r--examples/versioning/test_versioning.py81
4 files changed, 87 insertions, 99 deletions
diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst
index 228a85c1f..e4bd134be 100644
--- a/doc/build/changelog/changelog_09.rst
+++ b/doc/build/changelog/changelog_09.rst
@@ -13,6 +13,12 @@
:version: 0.9.0b2
.. change::
+ :tags: bug, examples
+
+ Fixed bug which prevented history_meta recipe from working with
+ joined inheritance schemes more than one level deep.
+
+ .. change::
:tags: bug, orm, sql, sqlite
:tickets: 2858
diff --git a/examples/versioning/_lib.py b/examples/versioning/_lib.py
deleted file mode 100644
index 9132f9b35..000000000
--- a/examples/versioning/_lib.py
+++ /dev/null
@@ -1,96 +0,0 @@
-"""copy of ComparableEntity and eq_() from test.lib.
-
-This is just to support running the example outside of
-the SQLA testing environment which is no longer part of
-SQLAlchemy as of 0.7.
-
-"""
-
-import sqlalchemy as sa
-from sqlalchemy import exc as sa_exc
-
-
-def eq_(a, b, msg=None):
- """Assert a == b, with repr messaging on failure."""
- assert a == b, msg or "%r != %r" % (a, b)
-
-_repr_stack = set()
-class BasicEntity(object):
- def __init__(self, **kw):
- for key, value in kw.items():
- setattr(self, key, value)
-
- def __repr__(self):
- if id(self) in _repr_stack:
- return object.__repr__(self)
- _repr_stack.add(id(self))
- try:
- return "%s(%s)" % (
- (self.__class__.__name__),
- ', '.join(["%s=%r" % (key, getattr(self, key))
- for key in sorted(self.__dict__.keys())
- if not key.startswith('_')]))
- finally:
- _repr_stack.remove(id(self))
-
-_recursion_stack = set()
-class ComparableEntity(BasicEntity):
- def __hash__(self):
- return hash(self.__class__)
-
- def __ne__(self, other):
- return not self.__eq__(other)
-
- def __eq__(self, other):
- """'Deep, sparse compare.
-
- Deeply compare two entities, following the non-None attributes of the
- non-persisted object, if possible.
-
- """
- if other is self:
- return True
- elif not self.__class__ == other.__class__:
- return False
-
- if id(self) in _recursion_stack:
- return True
- _recursion_stack.add(id(self))
-
- try:
- # pick the entity thats not SA persisted as the source
- try:
- self_key = sa.orm.attributes.instance_state(self).key
- except sa.orm.exc.NO_STATE:
- self_key = None
-
- if other is None:
- a = self
- b = other
- elif self_key is not None:
- a = other
- b = self
- else:
- a = self
- b = other
-
- for attr in a.__dict__.keys():
- if attr.startswith('_'):
- continue
- value = getattr(a, attr)
-
- try:
- # handle lazy loader errors
- battr = getattr(b, attr)
- except (AttributeError, sa_exc.UnboundExecutionError):
- return False
-
- if hasattr(value, '__iter__'):
- if list(value) != list(battr):
- return False
- else:
- if value is not None and value != battr:
- return False
- return True
- finally:
- _recursion_stack.remove(id(self))
diff --git a/examples/versioning/history_meta.py b/examples/versioning/history_meta.py
index 088326d8a..5b0f1920f 100644
--- a/examples/versioning/history_meta.py
+++ b/examples/versioning/history_meta.py
@@ -25,6 +25,7 @@ def _history_mapper(local_mapper):
polymorphic_on = None
super_fks = []
+
if not super_mapper or local_mapper.local_table is not super_mapper.local_table:
cols = []
for column in local_mapper.local_table.c:
@@ -43,7 +44,7 @@ def _history_mapper(local_mapper):
polymorphic_on = col
if super_mapper:
- super_fks.append(('version', super_history_mapper.base_mapper.local_table.c.version))
+ super_fks.append(('version', super_history_mapper.local_table.c.version))
cols.append(Column('version', Integer, primary_key=True, autoincrement=False))
else:
cols.append(Column('version', Integer, primary_key=True, autoincrement=False))
diff --git a/examples/versioning/test_versioning.py b/examples/versioning/test_versioning.py
index 9aaf7325b..c1d9c9878 100644
--- a/examples/versioning/test_versioning.py
+++ b/examples/versioning/test_versioning.py
@@ -3,7 +3,8 @@ from sqlalchemy.ext.declarative import declarative_base
from .history_meta import Versioned, versioned_session
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import clear_mappers, Session, deferred, relationship
-from ._lib import ComparableEntity, eq_
+from sqlalchemy.testing import AssertsCompiledSQL, eq_
+from sqlalchemy.testing.entities import BasicEntity, ComparableEntity
engine = None
@@ -12,7 +13,9 @@ def setup():
global engine
engine = create_engine('sqlite://', echo=True)
-class TestVersioning(TestCase):
+class TestVersioning(TestCase, AssertsCompiledSQL):
+ __dialect__ = 'default'
+
def setUp(self):
self.session = Session(engine)
self.Base = declarative_base()
@@ -218,6 +221,80 @@ class TestVersioning(TestCase):
]
)
+ def test_joined_inheritance_multilevel(self):
+ class BaseClass(Versioned, self.Base, ComparableEntity):
+ __tablename__ = 'basetable'
+
+ id = Column(Integer, primary_key=True)
+ name = Column(String(50))
+ type = Column(String(20))
+
+ __mapper_args__ = {'polymorphic_on': type,
+ 'polymorphic_identity': 'base'}
+
+ class SubClass(BaseClass):
+ __tablename__ = 'subtable'
+
+ id = Column(Integer, primary_key=True)
+ base_id = Column(Integer, ForeignKey('basetable.id'))
+ subdata1 = Column(String(50))
+
+ __mapper_args__ = {'polymorphic_identity': 'sub'}
+
+ class SubSubClass(SubClass):
+ __tablename__ = 'subsubtable'
+
+ id = Column(Integer, ForeignKey('subtable.id'), primary_key=True)
+ subdata2 = Column(String(50))
+
+ __mapper_args__ = {'polymorphic_identity': 'subsub'}
+
+ self.create_tables()
+
+ SubSubHistory = SubSubClass.__history_mapper__.class_
+ sess = self.session
+ q = sess.query(SubSubHistory)
+ self.assert_compile(
+ q,
+ "SELECT subsubtable_history.id AS subsubtable_history_id, "
+ "subtable_history.id AS subtable_history_id, "
+ "basetable_history.id AS basetable_history_id, "
+ "basetable_history.name AS basetable_history_name, "
+ "basetable_history.type AS basetable_history_type, "
+ "subsubtable_history.version AS subsubtable_history_version, "
+ "subtable_history.version AS subtable_history_version, "
+ "basetable_history.version AS basetable_history_version, "
+ "subtable_history.base_id AS subtable_history_base_id, "
+ "subtable_history.subdata1 AS subtable_history_subdata1, "
+ "subsubtable_history.subdata2 AS subsubtable_history_subdata2 "
+ "FROM basetable_history "
+ "JOIN subtable_history "
+ "ON basetable_history.id = subtable_history.base_id "
+ "AND basetable_history.version = subtable_history.version "
+ "JOIN subsubtable_history ON subtable_history.id = "
+ "subsubtable_history.id AND subtable_history.version = subsubtable_history.version"
+ )
+
+ ssc = SubSubClass(name='ss1', subdata1='sd1', subdata2='sd2')
+ sess.add(ssc)
+ sess.commit()
+ eq_(
+ sess.query(SubSubHistory).all(),
+ []
+ )
+ ssc.subdata1 = 'sd11'
+ ssc.subdata2 = 'sd22'
+ sess.commit()
+ eq_(
+ sess.query(SubSubHistory).all(),
+ [SubSubHistory(name='ss1', subdata1='sd1',
+ subdata2='sd2', type='subsub', version=1)]
+ )
+ eq_(ssc, SubSubClass(name='ss1', subdata1='sd11',
+ subdata2='sd22', version=2))
+
+
+
def test_single_inheritance(self):
class BaseClass(Versioned, self.Base, ComparableEntity):
__tablename__ = 'basetable'