diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-11-26 14:58:44 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-11-26 14:58:44 -0500 |
commit | de62497b03274c860ea2554dfbacb3064ce02c19 (patch) | |
tree | 59d01333280027a4fc12a371b12838b0cf454377 | |
parent | a88be57fb7ef96e914d8a7fb90de375ec7f0befd (diff) | |
download | sqlalchemy-de62497b03274c860ea2554dfbacb3064ce02c19.tar.gz |
- Updated the :ref:`examples_versioned_history` example such that
mapped columns are re-mapped to
match column names as well as grouping of columns; in particular,
this allows columns that are explicitly grouped in a same-column-named
joined inheritance scenario to be mapped in the same way in the
history mappings, avoiding warnings added in the 0.9 series
regarding this pattern and allowing the same view of attribute
keys.
-rw-r--r-- | doc/build/changelog/changelog_09.rst | 13 | ||||
-rw-r--r-- | doc/build/orm/examples.rst | 2 | ||||
-rw-r--r-- | examples/versioned_history/history_meta.py | 45 | ||||
-rw-r--r-- | examples/versioned_history/test_versioning.py | 73 |
4 files changed, 112 insertions, 21 deletions
diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst index ef0277935..f10d48273 100644 --- a/doc/build/changelog/changelog_09.rst +++ b/doc/build/changelog/changelog_09.rst @@ -17,6 +17,19 @@ :tags: bug, examples :versions: 1.0.0 + Updated the :ref:`examples_versioned_history` example such that + mapped columns are re-mapped to + match column names as well as grouping of columns; in particular, + this allows columns that are explicitly grouped in a same-column-named + joined inheritance scenario to be mapped in the same way in the + history mappings, avoiding warnings added in the 0.9 series + regarding this pattern and allowing the same view of attribute + keys. + + .. change:: + :tags: bug, examples + :versions: 1.0.0 + Fixed a bug in the examples/generic_assocaitions/discriminator_on_association.py example, where the subclasses of AddressAssociation were not being mapped as "single table inheritance", leading to problems when trying diff --git a/doc/build/orm/examples.rst b/doc/build/orm/examples.rst index b820dba9f..8803e1c34 100644 --- a/doc/build/orm/examples.rst +++ b/doc/build/orm/examples.rst @@ -79,6 +79,8 @@ XML Persistence Versioning Objects ------------------------ +.. _examples_versioned_history: + Versioning with a History Table ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/examples/versioned_history/history_meta.py b/examples/versioned_history/history_meta.py index f10444e52..6d7b137eb 100644 --- a/examples/versioned_history/history_meta.py +++ b/examples/versioned_history/history_meta.py @@ -4,7 +4,7 @@ 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 @@ -36,14 +36,20 @@ 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 + 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 @@ -64,6 +70,13 @@ def _history_mapper(local_mapper): if column is local_mapper.polymorphic_on: polymorphic_on = col + 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) + if super_mapper: super_fks.append( ( @@ -71,9 +84,6 @@ def _history_mapper(local_mapper): ) ) - version_meta = {"version_meta": True} # add column.info to identify - # columns specific to versioning - # "version" stores the integer version id. This column is # required. cols.append( @@ -84,9 +94,10 @@ def _history_mapper(local_mapper): # "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))) @@ -108,17 +119,25 @@ 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: diff --git a/examples/versioned_history/test_versioning.py b/examples/versioned_history/test_versioning.py index ed6935eb7..dde73a5ae 100644 --- a/examples/versioned_history/test_versioning.py +++ b/examples/versioned_history/test_versioning.py @@ -4,11 +4,16 @@ 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 @@ -226,7 +231,10 @@ class TestVersioning(TestCase, AssertsCompiledSQL): 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)) @@ -235,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'} @@ -317,7 +326,10 @@ class TestVersioning(TestCase, AssertsCompiledSQL): 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)) @@ -338,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, " @@ -352,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, " @@ -387,7 +402,49 @@ class TestVersioning(TestCase, AssertsCompiledSQL): 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): |