- 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.
This commit is contained in:
Mike Bayer
2014-11-26 14:58:44 -05:00
parent a88be57fb7
commit de62497b03
4 changed files with 112 additions and 21 deletions
+13
View File
@@ -13,6 +13,19 @@
.. changelog::
:version: 0.9.9
.. change::
: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
+2
View File
@@ -79,6 +79,8 @@ XML Persistence
Versioning Objects
------------------------
.. _examples_versioned_history:
Versioning with a History Table
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+32 -13
View File
@@ -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:
+65 -8
View File
@@ -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):