summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-11-26 14:58:44 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2014-11-26 14:58:44 -0500
commitde62497b03274c860ea2554dfbacb3064ce02c19 (patch)
tree59d01333280027a4fc12a371b12838b0cf454377
parenta88be57fb7ef96e914d8a7fb90de375ec7f0befd (diff)
downloadsqlalchemy-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.rst13
-rw-r--r--doc/build/orm/examples.rst2
-rw-r--r--examples/versioned_history/history_meta.py45
-rw-r--r--examples/versioned_history/test_versioning.py73
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):