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
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)
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)
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
-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):
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):
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):
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_
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()