Return to Snippet

Revision: 3881
at September 27, 2007 23:54 by arski7


Initial Code
"""
Test of Versionable using extensions

Why does the before_update or before_insert not cause the
    items added to the lists to save?  If you call save
    twice they do?
"""

from sqlalchemy import *
from sqlalchemy.orm import *

metadata = MetaData('sqlite://')

#######
class VersionExt(MapperExtension):
    """will update changes"""
    def before_update(self, mapper, connection, instance):
        instance._doversion()
        return EXT_CONTINUE
        
    def before_insert(self, mapper, connection, instance):
        instance._doversion()
        return EXT_CONTINUE


version_table = Table("version", metadata,
        Column("id", Integer, primary_key=True),
        Column('assoc_id', None, ForeignKey('version_associations.assoc_id')),
        Column("whatchanged", String(255), nullable=False),
        Column("version", Integer),
    )


## association table
version_associations_table = Table("version_associations", metadata, 
    Column('assoc_id', Integer, primary_key=True),
    Column('type', String(50), nullable=False),
    Column('version', Integer, default=0),
)

class Version(object):
    def __init__(self, chg):
        self.whatchanged = chg
    member = property(lambda self: getattr(self.association, '_backref_%s' % self.association.type))

class VersionAssoc(object):
    def __init__(self, name):
        self.type = name
    
def versionable(cls, name, uselist=True):
    """versionable 'interface'.
    
  
    """
    mapper = class_mapper(cls)
    table = mapper.local_table
    mapper.add_property('version_rel', relation(VersionAssoc, backref='_backref_%s' % table.name))

    if uselist:
        # list based property decorator
        def get(self):
            if self.version_rel is None:
                self.version_rel = VersionAssoc(table.name)
            return self.version_rel.versions
        setattr(cls, name, property(get))
    else:
        # scalar based property decorator
        def get(self):
            return self.version_rel.versions[0]
        def set(self, value):
            if self.version_rel is None:
                self.version_rel = VersionAssoc(table.name)
            self.version_rel.versions = [value]
        setattr(cls, name, property(get, set))
        
mapper(Version, version_table)

mapper(VersionAssoc, version_associations_table, properties={
    'versions':relation(Version, backref='association'),
})

######
# sample # 1, users

users = Table("users", metadata, 
    Column('id', Integer, primary_key=True),
    Column('name', String(50), nullable=False),
    # this column ties the users table into the change association
    Column('assoc_id', None, ForeignKey('version_associations.assoc_id'))
    )
    
class User(object):
    def _doversion(self):
        v1 = Version('what was changed?')
        self.versions.append(v1)
        v1.version = 1
        print ' in _doversion '

mapper(User, users, extension=VersionExt())
versionable(User, 'versions', uselist=True)

######
# use it !
metadata.create_all()

u1 = User()
u1.name = 'bob'


sess = create_session()
sess.save(u1)
sess.flush()
# uncommenting these saves causes the versions to now be saved?
# sess.save(u1)
# sess.flush()
sess.clear()

# query objects, get their versions

bob = sess.query(User).filter_by(name='bob').first()
print [v.whatchanged for v in bob.versions] 
if bob.versions:
    print 'bob changed: %s ct = %s ' % (bob.versions[0].whatchanged, len(bob.versions))
else:
    print 'no versions'

Initial URL


Initial Description
Not sure if this is working or not?

Initial Title
SqlAlchemy Versionable

Initial Tags


Initial Language
Python