From 82e4bc2f52f2d420842819d0ffe548ca968bf54e Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sun, 22 Jan 2012 19:03:06 -0500 Subject: [PATCH] - [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] --- CHANGES | 6 +++++ examples/versioning/__init__.py | 32 +++++++------------------- examples/versioning/history_meta.py | 31 ++++++++++++++----------- examples/versioning/test_versioning.py | 8 +++---- 4 files changed, 34 insertions(+), 43 deletions(-) diff --git a/CHANGES b/CHANGES index 60de9ec80b..53220bfe59 100644 --- 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 diff --git a/examples/versioning/__init__.py b/examples/versioning/__init__.py index 61b8f13c65..9909009402 100644 --- a/examples/versioning/__init__.py +++ b/examples/versioning/__init__.py @@ -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 diff --git a/examples/versioning/history_meta.py b/examples/versioning/history_meta.py index 9544451eec..1226a8f62e 100644 --- a/examples/versioning/history_meta.py +++ b/examples/versioning/history_meta.py @@ -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): diff --git a/examples/versioning/test_versioning.py b/examples/versioning/test_versioning.py index d91f12037a..389dba9187 100644 --- a/examples/versioning/test_versioning.py +++ b/examples/versioning/test_versioning.py @@ -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() -- 2.47.2