`0001from sqlobject import *
0002from datetime import datetime
0003
0004class Version(SQLObject):
0005 def restore(self):
0006 values = self.sqlmeta.asDict()
0007 del values['id']
0008 del values['masterID']
0009 del values['dateArchived']
0010 for col in self.extraCols:
0011 del values[col]
0012 self.masterClass.get(self.masterID).set(**values)
0013
0014 def nextVersion(self):
0015 version = self.select(AND(self.q.masterID == self.masterID, self.q.id > self.id), orderBy=self.q.id)
0016 if version.count():
0017 return version[0]
0018 else:
0019 return self.master
0020
0021 def getChangedFields(self):
0022 next = self.nextVersion()
0023 columns = self.masterClass.sqlmeta.columns
0024 fields = []
0025 for column in columns:
0026 if column not in ["dateArchived", "id", "masterID"]:
0027 if getattr(self, column) != getattr(next, column):
0028 fields.append(column.title())
0029
0030 return fields
0031
0032 @classmethod
0033 def select(cls, clause=None, *args, **kw):
0034 if not getattr(cls, '_connection', None):
0035 cls._connection = cls.masterClass._connection
0036 return super(Version, cls).select(clause, *args, **kw)
0037
0038 def __getattr__(self, attr):
0039 if attr in self.__dict__:
0040 return self.__dict__[attr]
0041 else:
0042 return getattr(self.master, attr)
0043
0044def getColumns(columns, cls):
0045 for column, defi in cls.sqlmeta.columnDefinitions.items():
0046 if column.endswith("ID") and isinstance(defi, ForeignKey):
0047 column = column[:-2]
0048
0049 #remove incompatible constraints
0050 kwds = dict(defi._kw)
0051 for kw in ["alternateID", "unique"]:
0052 if kw in kwds: del kwds[kw]
0053 columns[column] = defi.__class__(**kwds)
0054
0055 #ascend heirarchy
0056 if cls.sqlmeta.parentClass:
0057 getColumns(columns, cls.sqlmeta.parentClass)
0058
0059
0060class Versioning(object):
0061 def __init__(self, extraCols = None):
0062 if extraCols:
0063 self.extraCols = extraCols
0064 else:
0065 self.extraCols = {}
0066 pass
0067
0068 def __addtoclass__(self, soClass, name):
0069 self.name = name
0070 self.soClass = soClass
0071
0072 attrs = {'dateArchived': DateTimeCol(default=datetime.now),
0073 'master': ForeignKey(self.soClass.__name__),
0074 'masterClass' : self.soClass,
0075 'extraCols' : self.extraCols
0076 }
0077
0078 getColumns (attrs, self.soClass)
0079
0080 attrs.update(self.extraCols)
0081
0082 self.versionClass = type(self.soClass.__name__+'Versions',
0083 (Version,),
0084 attrs)
0085
0086 if '_connection' in self.soClass.__dict__:
0087 self.versionClass._connection = self.soClass.__dict__['_connection']
0088
0089 events.listen(self.createTable,
0090 soClass, events.CreateTableSignal)
0091 events.listen(self.rowUpdate, soClass,
0092 events.RowUpdateSignal)
0093
0094 def createVersionTable(self, cls, conn):
0095 self.versionClass.createTable(ifNotExists=True, connection=conn)
0096
0097 def createTable(self, soClass, connection, extra_sql, post_funcs):
0098 assert soClass is self.soClass
0099 post_funcs.append(self.createVersionTable)
0100
0101 def rowUpdate(self, instance, kwargs):
0102 if instance.childName and instance.childName != self.soClass.__name__:
0103 return #if you want your child class versioned, version it.
0104
0105 values = instance.sqlmeta.asDict()
0106 del values['id']
0107 values['masterID'] = instance.id
0108 self.versionClass(connection=instance._connection, **values)
0109
0110 def __get__(self, obj, type=None):
0111 if obj is None:
0112 return self
0113 return self.versionClass.select(
0114 self.versionClass.q.masterID==obj.id, connection=obj._connection)`