diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-12-07 17:48:32 -0500 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-12-07 17:48:32 -0500 |
| commit | 1b260c7959c9b89e6a3993d5d96bc6b0918a8fb0 (patch) | |
| tree | 59602c3151094de79d84c60391cf64e4a1b94084 /examples | |
| parent | 68f1bcc9da191fc72a943f2353c8d80a39bdb76a (diff) | |
| parent | c8817e608788799837a91b1d2616227594698d2b (diff) | |
| download | sqlalchemy-1b260c7959c9b89e6a3993d5d96bc6b0918a8fb0.tar.gz | |
Merge branch 'master' into ticket_3100
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/generic_associations/discriminator_on_association.py | 1 | ||||
| -rw-r--r-- | examples/versioned_history/history_meta.py | 104 | ||||
| -rw-r--r-- | examples/versioned_history/test_versioning.py | 173 |
3 files changed, 204 insertions, 74 deletions
diff --git a/examples/generic_associations/discriminator_on_association.py b/examples/generic_associations/discriminator_on_association.py index e03cfec00..7bb04cf85 100644 --- a/examples/generic_associations/discriminator_on_association.py +++ b/examples/generic_associations/discriminator_on_association.py @@ -84,6 +84,7 @@ class HasAddresses(object): "%sAddressAssociation" % name, (AddressAssociation, ), dict( + __tablename__=None, __mapper_args__={ "polymorphic_identity": discriminator } diff --git a/examples/versioned_history/history_meta.py b/examples/versioned_history/history_meta.py index f9e979a6a..6d7b137eb 100644 --- a/examples/versioned_history/history_meta.py +++ b/examples/versioned_history/history_meta.py @@ -4,19 +4,22 @@ from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.orm import mapper, attributes, object_mapper from sqlalchemy.orm.exc import UnmappedColumnError from sqlalchemy import Table, Column, ForeignKeyConstraint, Integer, DateTime -from sqlalchemy import event +from sqlalchemy import event, util import datetime from sqlalchemy.orm.properties import RelationshipProperty + def col_references_table(col, table): for fk in col.foreign_keys: if fk.references(table): return True return False + def _is_versioning_col(col): return "version_meta" in col.info + def _history_mapper(local_mapper): cls = local_mapper.class_ @@ -33,52 +36,77 @@ def _history_mapper(local_mapper): super_fks = [] def _col_copy(col): + orig = col col = col.copy() + orig.info['history_copy'] = col col.unique = False col.default = col.server_default = None return col - if not super_mapper or local_mapper.local_table is not super_mapper.local_table: + properties = util.OrderedDict() + if not super_mapper or \ + local_mapper.local_table is not super_mapper.local_table: cols = [] + version_meta = {"version_meta": True} # add column.info to identify + # columns specific to versioning + for column in local_mapper.local_table.c: if _is_versioning_col(column): continue col = _col_copy(column) - if super_mapper and col_references_table(column, super_mapper.local_table): - super_fks.append((col.key, list(super_history_mapper.local_table.primary_key)[0])) + if super_mapper and \ + col_references_table(column, super_mapper.local_table): + super_fks.append( + ( + col.key, + list(super_history_mapper.local_table.primary_key)[0] + ) + ) cols.append(col) if column is local_mapper.polymorphic_on: polymorphic_on = col - if super_mapper: - super_fks.append(('version', super_history_mapper.local_table.c.version)) + orig_prop = local_mapper.get_property_by_column(column) + # carry over column re-mappings + if len(orig_prop.columns) > 1 or \ + orig_prop.columns[0].key != orig_prop.key: + properties[orig_prop.key] = tuple( + col.info['history_copy'] for col in orig_prop.columns) - version_meta = {"version_meta": True} # add column.info to identify - # columns specific to versioning + if super_mapper: + super_fks.append( + ( + 'version', super_history_mapper.local_table.c.version + ) + ) # "version" stores the integer version id. This column is # required. - cols.append(Column('version', Integer, primary_key=True, - autoincrement=False, info=version_meta)) + cols.append( + Column( + 'version', Integer, primary_key=True, + autoincrement=False, info=version_meta)) # "changed" column stores the UTC timestamp of when the # history row was created. # This column is optional and can be omitted. - cols.append(Column('changed', DateTime, - default=datetime.datetime.utcnow, - info=version_meta)) + cols.append(Column( + 'changed', DateTime, + default=datetime.datetime.utcnow, + info=version_meta)) if super_fks: cols.append(ForeignKeyConstraint(*zip(*super_fks))) - table = Table(local_mapper.local_table.name + '_history', - local_mapper.local_table.metadata, - *cols, - schema=local_mapper.local_table.schema + table = Table( + local_mapper.local_table.name + '_history', + local_mapper.local_table.metadata, + *cols, + schema=local_mapper.local_table.schema ) else: # single table inheritance. take any additional columns that may have @@ -91,24 +119,33 @@ def _history_mapper(local_mapper): if super_history_mapper: bases = (super_history_mapper.class_,) + + if table is not None: + properties['changed'] = ( + (table.c.changed, ) + + tuple(super_history_mapper.attrs.changed.columns) + ) + else: bases = local_mapper.base_mapper.class_.__bases__ versioned_cls = type.__new__(type, "%sHistory" % cls.__name__, bases, {}) m = mapper( - versioned_cls, - table, - inherits=super_history_mapper, - polymorphic_on=polymorphic_on, - polymorphic_identity=local_mapper.polymorphic_identity - ) + versioned_cls, + table, + inherits=super_history_mapper, + polymorphic_on=polymorphic_on, + polymorphic_identity=local_mapper.polymorphic_identity, + properties=properties + ) cls.__history_mapper__ = m if not super_history_mapper: local_mapper.local_table.append_column( Column('version', Integer, default=1, nullable=False) ) - local_mapper.add_property("version", local_mapper.local_table.c.version) + local_mapper.add_property( + "version", local_mapper.local_table.c.version) class Versioned(object): @@ -126,6 +163,7 @@ def versioned_objects(iter): if hasattr(obj, '__history_mapper__'): yield obj + def create_version(obj, session, deleted=False): obj_mapper = object_mapper(obj) history_mapper = obj.__history_mapper__ @@ -137,7 +175,10 @@ def create_version(obj, session, deleted=False): obj_changed = False - for om, hm in zip(obj_mapper.iterate_to_root(), history_mapper.iterate_to_root()): + for om, hm in zip( + obj_mapper.iterate_to_root(), + history_mapper.iterate_to_root() + ): if hm.single: continue @@ -157,11 +198,12 @@ def create_version(obj, session, deleted=False): # in the case of single table inheritance, there may be # columns on the mapped table intended for the subclass only. # the "unmapped" status of the subclass column on the - # base class is a feature of the declarative module as of sqla 0.5.2. + # base class is a feature of the declarative module. continue - # expired object attributes and also deferred cols might not be in the - # dict. force it to load no matter what by using getattr(). + # expired object attributes and also deferred cols might not + # be in the dict. force it to load no matter what by + # using getattr(). if prop.key not in obj_state.dict: getattr(obj, prop.key) @@ -182,8 +224,9 @@ def create_version(obj, session, deleted=False): # check those too for prop in obj_mapper.iterate_properties: if isinstance(prop, RelationshipProperty) and \ - attributes.get_history(obj, prop.key, - passive=attributes.PASSIVE_NO_INITIALIZE).has_changes(): + attributes.get_history( + obj, prop.key, + passive=attributes.PASSIVE_NO_INITIALIZE).has_changes(): for p in prop.local_columns: if p.foreign_keys: obj_changed = True @@ -201,6 +244,7 @@ def create_version(obj, session, deleted=False): session.add(hist) obj.version += 1 + def versioned_session(session): @event.listens_for(session, 'before_flush') def before_flush(session, flush_context, instances): diff --git a/examples/versioned_history/test_versioning.py b/examples/versioned_history/test_versioning.py index 874223d62..dde73a5ae 100644 --- a/examples/versioned_history/test_versioning.py +++ b/examples/versioned_history/test_versioning.py @@ -1,20 +1,28 @@ -"""Unit tests illustrating usage of the ``history_meta.py`` module functions.""" +"""Unit tests illustrating usage of the ``history_meta.py`` +module functions.""" from unittest import TestCase from sqlalchemy.ext.declarative import declarative_base from .history_meta import Versioned, versioned_session -from sqlalchemy import create_engine, Column, Integer, String, ForeignKey, Boolean -from sqlalchemy.orm import clear_mappers, Session, deferred, relationship +from sqlalchemy import create_engine, Column, Integer, String, \ + ForeignKey, Boolean, select +from sqlalchemy.orm import clear_mappers, Session, deferred, relationship, \ + column_property from sqlalchemy.testing import AssertsCompiledSQL, eq_, assert_raises from sqlalchemy.testing.entities import ComparableEntity from sqlalchemy.orm import exc as orm_exc +import warnings + +warnings.simplefilter("error") engine = None + def setup_module(): global engine engine = create_engine('sqlite://', echo=True) + class TestVersioning(TestCase, AssertsCompiledSQL): __dialect__ = 'default' @@ -52,14 +60,16 @@ class TestVersioning(TestCase, AssertsCompiledSQL): SomeClassHistory = SomeClass.__history_mapper__.class_ eq_( - sess.query(SomeClassHistory).filter(SomeClassHistory.version == 1).all(), + sess.query(SomeClassHistory).filter( + SomeClassHistory.version == 1).all(), [SomeClassHistory(version=1, name='sc1')] ) sc.name = 'sc1modified2' eq_( - sess.query(SomeClassHistory).order_by(SomeClassHistory.version).all(), + sess.query(SomeClassHistory).order_by( + SomeClassHistory.version).all(), [ SomeClassHistory(version=1, name='sc1'), SomeClassHistory(version=2, name='sc1modified') @@ -76,7 +86,8 @@ class TestVersioning(TestCase, AssertsCompiledSQL): sess.commit() eq_( - sess.query(SomeClassHistory).order_by(SomeClassHistory.version).all(), + sess.query(SomeClassHistory).order_by( + SomeClassHistory.version).all(), [ SomeClassHistory(version=1, name='sc1'), SomeClassHistory(version=2, name='sc1modified') @@ -87,7 +98,8 @@ class TestVersioning(TestCase, AssertsCompiledSQL): sess.commit() eq_( - sess.query(SomeClassHistory).order_by(SomeClassHistory.version).all(), + sess.query(SomeClassHistory).order_by( + SomeClassHistory.version).all(), [ SomeClassHistory(version=1, name='sc1'), SomeClassHistory(version=2, name='sc1modified'), @@ -164,13 +176,13 @@ class TestVersioning(TestCase, AssertsCompiledSQL): SomeClassHistory = SomeClass.__history_mapper__.class_ eq_( - sess.query(SomeClassHistory.boole).order_by(SomeClassHistory.id).all(), + sess.query(SomeClassHistory.boole).order_by( + SomeClassHistory.id).all(), [(True, ), (None, )] ) eq_(sc.version, 3) - def test_deferred(self): """test versioning of unloaded, deferred columns.""" @@ -199,11 +211,11 @@ class TestVersioning(TestCase, AssertsCompiledSQL): SomeClassHistory = SomeClass.__history_mapper__.class_ eq_( - sess.query(SomeClassHistory).filter(SomeClassHistory.version == 1).all(), + sess.query(SomeClassHistory).filter( + SomeClassHistory.version == 1).all(), [SomeClassHistory(version=1, name='sc1', data='somedata')] ) - def test_joined_inheritance(self): class BaseClass(Versioned, self.Base, ComparableEntity): __tablename__ = 'basetable' @@ -212,12 +224,17 @@ class TestVersioning(TestCase, AssertsCompiledSQL): name = Column(String(50)) type = Column(String(20)) - __mapper_args__ = {'polymorphic_on': type, 'polymorphic_identity': 'base'} + __mapper_args__ = { + 'polymorphic_on': type, + 'polymorphic_identity': 'base'} class SubClassSeparatePk(BaseClass): __tablename__ = 'subtable1' - id = Column(Integer, primary_key=True) + id = column_property( + Column(Integer, primary_key=True), + BaseClass.id + ) base_id = Column(Integer, ForeignKey('basetable.id')) subdata1 = Column(String(50)) @@ -226,7 +243,8 @@ class TestVersioning(TestCase, AssertsCompiledSQL): class SubClassSamePk(BaseClass): __tablename__ = 'subtable2' - id = Column(Integer, ForeignKey('basetable.id'), primary_key=True) + id = Column( + Integer, ForeignKey('basetable.id'), primary_key=True) subdata2 = Column(String(50)) __mapper_args__ = {'polymorphic_identity': 'same'} @@ -246,38 +264,50 @@ class TestVersioning(TestCase, AssertsCompiledSQL): sess.commit() BaseClassHistory = BaseClass.__history_mapper__.class_ - SubClassSeparatePkHistory = SubClassSeparatePk.__history_mapper__.class_ + SubClassSeparatePkHistory = \ + SubClassSeparatePk.__history_mapper__.class_ SubClassSamePkHistory = SubClassSamePk.__history_mapper__.class_ eq_( sess.query(BaseClassHistory).order_by(BaseClassHistory.id).all(), [ - SubClassSeparatePkHistory(id=1, name='sep1', type='sep', version=1), + SubClassSeparatePkHistory( + id=1, name='sep1', type='sep', version=1), BaseClassHistory(id=2, name='base1', type='base', version=1), - SubClassSamePkHistory(id=3, name='same1', type='same', version=1) + SubClassSamePkHistory( + id=3, name='same1', type='same', version=1) ] ) same1.subdata2 = 'same1subdatamod2' eq_( - sess.query(BaseClassHistory).order_by(BaseClassHistory.id, BaseClassHistory.version).all(), + sess.query(BaseClassHistory).order_by( + BaseClassHistory.id, BaseClassHistory.version).all(), [ - SubClassSeparatePkHistory(id=1, name='sep1', type='sep', version=1), + SubClassSeparatePkHistory( + id=1, name='sep1', type='sep', version=1), BaseClassHistory(id=2, name='base1', type='base', version=1), - SubClassSamePkHistory(id=3, name='same1', type='same', version=1), - SubClassSamePkHistory(id=3, name='same1', type='same', version=2) + SubClassSamePkHistory( + id=3, name='same1', type='same', version=1), + SubClassSamePkHistory( + id=3, name='same1', type='same', version=2) ] ) base1.name = 'base1mod2' eq_( - sess.query(BaseClassHistory).order_by(BaseClassHistory.id, BaseClassHistory.version).all(), + sess.query(BaseClassHistory).order_by( + BaseClassHistory.id, BaseClassHistory.version).all(), [ - SubClassSeparatePkHistory(id=1, name='sep1', type='sep', version=1), + SubClassSeparatePkHistory( + id=1, name='sep1', type='sep', version=1), BaseClassHistory(id=2, name='base1', type='base', version=1), - BaseClassHistory(id=2, name='base1mod', type='base', version=2), - SubClassSamePkHistory(id=3, name='same1', type='same', version=1), - SubClassSamePkHistory(id=3, name='same1', type='same', version=2) + BaseClassHistory( + id=2, name='base1mod', type='base', version=2), + SubClassSamePkHistory( + id=3, name='same1', type='same', version=1), + SubClassSamePkHistory( + id=3, name='same1', type='same', version=2) ] ) @@ -289,13 +319,17 @@ class TestVersioning(TestCase, AssertsCompiledSQL): name = Column(String(50)) type = Column(String(20)) - __mapper_args__ = {'polymorphic_on': type, - 'polymorphic_identity': 'base'} + __mapper_args__ = { + 'polymorphic_on': type, + 'polymorphic_identity': 'base'} class SubClass(BaseClass): __tablename__ = 'subtable' - id = Column(Integer, primary_key=True) + id = column_property( + Column(Integer, primary_key=True), + BaseClass.id + ) base_id = Column(Integer, ForeignKey('basetable.id')) subdata1 = Column(String(50)) @@ -316,12 +350,18 @@ class TestVersioning(TestCase, AssertsCompiledSQL): 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, " + "subsubtable_history.changed AS subsubtable_history_changed, " + "subtable_history.changed AS subtable_history_changed, " + "basetable_history.changed AS basetable_history_changed, " + "basetable_history.name AS basetable_history_name, " "basetable_history.type AS basetable_history_type, " @@ -330,9 +370,6 @@ class TestVersioning(TestCase, AssertsCompiledSQL): "subtable_history.version AS subtable_history_version, " "basetable_history.version AS basetable_history_version, " - "subsubtable_history.changed AS subsubtable_history_changed, " - "subtable_history.changed AS subtable_history_changed, " - "basetable_history.changed AS basetable_history_changed, " "subtable_history.base_id AS subtable_history_base_id, " "subtable_history.subdata1 AS subtable_history_subdata1, " @@ -342,7 +379,8 @@ class TestVersioning(TestCase, AssertsCompiledSQL): "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" + "subsubtable_history.id AND subtable_history.version = " + "subsubtable_history.version" ) ssc = SubSubClass(name='ss1', subdata1='sd1', subdata2='sd2') @@ -360,10 +398,53 @@ class TestVersioning(TestCase, AssertsCompiledSQL): [SubSubHistory(name='ss1', subdata1='sd1', subdata2='sd2', type='subsub', version=1)] ) - eq_(ssc, SubSubClass(name='ss1', subdata1='sd11', - subdata2='sd22', version=2)) + eq_(ssc, SubSubClass( + name='ss1', subdata1='sd11', + subdata2='sd22', version=2)) + + def test_joined_inheritance_changed(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, ForeignKey('basetable.id'), primary_key=True) + + __mapper_args__ = {'polymorphic_identity': 'sep'} + + self.create_tables() + + BaseClassHistory = BaseClass.__history_mapper__.class_ + SubClassHistory = SubClass.__history_mapper__.class_ + sess = self.session + s1 = SubClass(name='s1') + sess.add(s1) + sess.commit() + + s1.name = 's2' + sess.commit() + actual_changed_base = sess.scalar( + select([BaseClass.__history_mapper__.local_table.c.changed])) + actual_changed_sub = sess.scalar( + select([SubClass.__history_mapper__.local_table.c.changed])) + h1 = sess.query(BaseClassHistory).first() + eq_(h1.changed, actual_changed_base) + eq_(h1.changed, actual_changed_sub) + h1 = sess.query(SubClassHistory).first() + eq_(h1.changed, actual_changed_base) + eq_(h1.changed, actual_changed_sub) def test_single_inheritance(self): class BaseClass(Versioned, self.Base, ComparableEntity): @@ -372,8 +453,9 @@ class TestVersioning(TestCase, AssertsCompiledSQL): id = Column(Integer, primary_key=True) name = Column(String(50)) type = Column(String(50)) - __mapper_args__ = {'polymorphic_on': type, - 'polymorphic_identity': 'base'} + __mapper_args__ = { + 'polymorphic_on': type, + 'polymorphic_identity': 'base'} class SubClass(BaseClass): @@ -396,8 +478,8 @@ class TestVersioning(TestCase, AssertsCompiledSQL): SubClassHistory = SubClass.__history_mapper__.class_ eq_( - sess.query(BaseClassHistory).order_by(BaseClassHistory.id, - BaseClassHistory.version).all(), + sess.query(BaseClassHistory).order_by( + BaseClassHistory.id, BaseClassHistory.version).all(), [BaseClassHistory(id=1, name='b1', type='base', version=1)] ) @@ -405,11 +487,12 @@ class TestVersioning(TestCase, AssertsCompiledSQL): b1.name = 'b1modified2' eq_( - sess.query(BaseClassHistory).order_by(BaseClassHistory.id, - BaseClassHistory.version).all(), + sess.query(BaseClassHistory).order_by( + BaseClassHistory.id, BaseClassHistory.version).all(), [ BaseClassHistory(id=1, name='b1', type='base', version=1), - BaseClassHistory(id=1, name='b1modified', type='base', version=2), + BaseClassHistory( + id=1, name='b1modified', type='base', version=2), SubClassHistory(id=2, name='s1', type='sub', version=1) ] ) @@ -475,14 +558,16 @@ class TestVersioning(TestCase, AssertsCompiledSQL): assert sc.version == 2 eq_( - sess.query(SomeClassHistory).filter(SomeClassHistory.version == 1).all(), + sess.query(SomeClassHistory).filter( + SomeClassHistory.version == 1).all(), [SomeClassHistory(version=1, name='sc1', related_id=None)] ) sc.related = None eq_( - sess.query(SomeClassHistory).order_by(SomeClassHistory.version).all(), + sess.query(SomeClassHistory).order_by( + SomeClassHistory.version).all(), [ SomeClassHistory(version=1, name='sc1', related_id=None), SomeClassHistory(version=2, name='sc1', related_id=sr1.id) |
