]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- [feature] Simplified the versioning example
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 23 Jan 2012 00:03:06 +0000 (19:03 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 23 Jan 2012 00:03:06 +0000 (19:03 -0500)
a bit to use a declarative mixin as well
as an event listener, instead of a metaclass +
SessionExtension.  [ticket:2313]

CHANGES
examples/versioning/__init__.py
examples/versioning/history_meta.py
examples/versioning/test_versioning.py

diff --git a/CHANGES b/CHANGES
index 60de9ec80bc6254f85f9f27b98515b2cfb7aa1b8..53220bfe599ebf28a7c05641b54064eab2324d7a 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -78,6 +78,12 @@ CHANGES
     series of import restrictions only.
     [ticket:2348]
 
+- examples
+  - [feature] Simplified the versioning example
+    a bit to use a declarative mixin as well 
+    as an event listener, instead of a metaclass +
+    SessionExtension.  [ticket:2313]
+
 0.7.4
 =====
 - orm
index 61b8f13c65cfe340e6ef4727c9b5953d3113a4b9..99090094024af28e03f19cab9675d7da407b2fc3 100644 (file)
@@ -13,12 +13,11 @@ be run via nose::
 
 A fragment of example usage, using declarative::
 
-    from history_meta import VersionedMeta, VersionedListener
+    from history_meta import Versioned, versioned_session
 
-    Base = declarative_base(metaclass=VersionedMeta, bind=engine)
-    Session = sessionmaker(extension=VersionedListener())
+    Base = declarative_base()
 
-    class SomeClass(Base):
+    class SomeClass(Versioned, Base):
         __tablename__ = 'sometable'
 
         id = Column(Integer, primary_key=True)
@@ -27,6 +26,9 @@ A fragment of example usage, using declarative::
         def __eq__(self, other):
             assert type(other) is SomeClass and other.id == self.id
 
+    Session = sessionmaker(bind=engine)
+    versioned_session(Session)
+
     sess = Session()
     sc = SomeClass(name='sc1')
     sess.add(sc)
@@ -44,26 +46,8 @@ A fragment of example usage, using declarative::
                 all() \\
                 == [SomeClassHistory(version=1, name='sc1')]
 
-To apply ``VersionedMeta`` to a subset of classes (probably more typical), the
-metaclass can be applied on a per-class basis::
-
-    from history_meta import VersionedMeta, VersionedListener
-
-    Base = declarative_base(bind=engine)
-
-    class SomeClass(Base):
-        __tablename__ = 'sometable'
-
-        # ...
-
-    class SomeVersionedClass(Base):
-        __metaclass__ = VersionedMeta
-        __tablename__ = 'someothertable'
-
-        # ...
-
-The ``VersionedMeta`` is a declarative metaclass - to use the extension with
-plain mappers, the ``_history_mapper`` function can be applied::
+The ``Versioned`` mixin is designed to work with declarative.  To use the extension with
+classical mappers, the ``_history_mapper`` function can be applied::
 
     from history_meta import _history_mapper
 
index 9544451eecd2d3611074c61f643d6e30ee7b9f2e..1226a8f62e6f96589273077ad336a806b7a29e5b 100644 (file)
@@ -1,8 +1,8 @@
-from sqlalchemy.ext.declarative import DeclarativeMeta
+from sqlalchemy.ext.declarative import declared_attr
 from sqlalchemy.orm import mapper, class_mapper, attributes, object_mapper
 from sqlalchemy.orm.exc import UnmappedClassError, UnmappedColumnError
 from sqlalchemy import Table, Column, ForeignKeyConstraint, Integer
-from sqlalchemy.orm.interfaces import SessionExtension
+from sqlalchemy import event
 from sqlalchemy.orm.properties import RelationshipProperty
 
 def col_references_table(col, table):
@@ -80,18 +80,20 @@ def _history_mapper(local_mapper):
     cls.__history_mapper__ = m
 
     if not super_history_mapper:
-        cls.version = Column('version', Integer, default=1, nullable=False)
-
+        local_mapper.local_table.append_column(
+            Column('version', Integer, default=1, nullable=False)
+        )
+        local_mapper.add_property("version", local_mapper.local_table.c.version)
 
-class VersionedMeta(DeclarativeMeta):
-    def __init__(cls, classname, bases, dict_):
-        DeclarativeMeta.__init__(cls, classname, bases, dict_)
 
-        try:
-            mapper = class_mapper(cls)
-            _history_mapper(mapper)
-        except UnmappedClassError:
-            pass
+class Versioned(object):
+    @declared_attr
+    def __mapper_cls__(cls):
+        def map(cls, *arg, **kw):
+            mp = mapper(cls, *arg, **kw)
+            _history_mapper(mp)
+            return mp
+        return map
 
 
 def versioned_objects(iter):
@@ -169,8 +171,9 @@ def create_version(obj, session, deleted = False):
     session.add(hist)
     obj.version += 1
 
-class VersionedListener(SessionExtension):
-    def before_flush(self, session, flush_context, instances):
+def versioned_session(session):
+    @event.listens_for(session, 'before_flush')
+    def before_flush(session, flush_context, instances):
         for obj in versioned_objects(session.dirty):
             create_version(obj, session)
         for obj in versioned_objects(session.deleted):
index d91f12037a18a56c80c854d88d156d57f348586f..389dba91876d83dbccd7ada4572f0ada4b98d099 100644 (file)
@@ -1,6 +1,6 @@
 from unittest import TestCase
 from sqlalchemy.ext.declarative import declarative_base
-from history_meta import VersionedMeta, VersionedListener
+from history_meta import Versioned, versioned_session
 from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
 from sqlalchemy.orm import clear_mappers, sessionmaker, deferred, relationship
 from _lib import ComparableEntity, eq_
@@ -13,10 +13,8 @@ class TestVersioning(TestCase):
     def setUp(self):
         global Base, Session, Versioned
         Base = declarative_base(bind=engine)
-        class Versioned(object):
-            __metaclass__ = VersionedMeta
-            _decl_class_registry = Base._decl_class_registry
-        Session = sessionmaker(extension=VersionedListener())
+        Session = sessionmaker()
+        versioned_session(Session)
 
     def tearDown(self):
         clear_mappers()