[SQLObject] sqlobject, formencode and an object proxy.
SQLObject is a Python ORM.
Brought to you by:
ianbicking,
phd
From: David D. <cou...@gm...> - 2005-06-22 14:38:14
|
Here is a command proxy that I have been working on during lunch and after dinner on and off for a few weeks. I am at a point that I am not sure where to go next. The idea is that each sqlobject has a a formencode validator schema (VO) and a stub class that contains the logic for creating default values (DO). The Proxy class checks to see if the proxied object is a subclass of default when it is instanced. It then deals with lookups accordingly and creates a sqlobject instance for the class when the proxy is committed. The proxy then replaces the default object with the new sqlobject. I am now dealing with how to conveniently convert the values in the sqlobject to strings so they will validate with formencode. I know that I should be able to just call from_python on the schema. Is there and easy way to do this with the sqlobject? Also looking for comments on the methodology. I could just be barking up the wrong tree with the way I have put things together. This is the first OO project I have done. I have also attached the code to this email but I don't know if it will get through. from datetime import date import sqlobject import formencode from formencode import validators __connection__ =3D sqlobject.FirebirdConnection( host=3D'localhost', db=3D'C:\Program Files\Firebird\Firebird_1_5\examples\EMPLOYEE.FDB', user=3D'sysdba', passwd=3D'goalkick') class Proxy(object): =20 def __init__(self,proxyObj,vo,baseClass): """ Init internal values This traps gets and sets for local attributes the __getattr_and __setattr__ methods proxy them back up to the base class object to avoid recurs= ion these lists are also set on object to avoid recursion if you have to add additional attributes or methods please add them here""" =20 object.__setattr__(self, 'localAttrs',['memento','proxyObj','vo','baseClass','_isDefaultObj']) # don't know if this is needed but it is here anyway object.__setattr__(self, 'localMethods',['isDirty','commit','rollBa= ck']) # set up inital values self.memento =3D {} self.proxyObj =3D proxyObj self._isDefaultObj =3D isinstance(self.proxyObj,Default) self.vo =3D vo self.baseClass =3D baseClass =20 def __getattr__(self,attr): """ Trap for getting any attributes for object or proxied obj check to see if it is in the local attrs list by asking base object to avoid recursion. If is is a local attribute get it from base object. Otherwise check the memento then the proxied object.""" =20 if attr in object.__getattribute__(self,'localAttrs'): return object.__getattribute__(self,attr) elif attr in self.memento: return self.memento[attr] elif not self._isDefaultObj and attr in self.proxyObj._SO_columnDic= t: return self.proxyObj.__dict__['_SO_val_'+attr] elif self._isDefaultObj and attr in self.proxyObj.__dict__: return self.proxyObj.__dict__[attr] else: raise AttributeError('No attribute get '+attr) =20 def __setattr__(self,attr,value): """ Trap for getting any attributes for object or proxied obj =20 check to see if it is in the local attrs list by asking base object to avoid recursion. If is is a local attribute set it from base object otherwise check the memento and the proxied object if it is foud set it in the memento. If it isn't found then raise attibute error""" =20 if attr in object.__getattribute__(self,'localAttrs'): object.__setattr__(self, attr, value) elif attr in self.memento: self.memento[attr] =3D value elif not self._isDefaultObj and attr in self.proxyObj._SO_columnDic= t: self.memento[attr] =3D value elif self._isDefaultObj and attr in self.proxyObj.__dict__: self.memento[attr] =3D value else: raise AttributeError('No attribute set '+attr) =20 def isDirty(self): return len(self.memento.keys())>0 def commit(self): """ validate proxied object then commit to sqlobject build a dict of all intermediate values validate via the schema object check isinstance(self.proxyObj,BaseDefault) if it is create a new object from baseClass with values from the current proxyObj and Memento make new instance the proxyObj reset _isDefaultObj flag otherwise assign memento values to the proxied object""" =20 commitDict =3D dict() if not self._isDefaultObj: for attr in self.proxyObj._SO_columnDict: commitDict[attr] =3D self.proxyObj.__dict__['_SO_val_'+attr= ] commitDict.update(self.memento) # this is probably where you would want to check that # you have the current record by refreshing the sqlobject # and comparing updateDate dts self.proxyObj.set(**self.vo.to_python(commitDict)) else: commitDict.update(self.proxyObj.__dict__) commitDict.update(self.memento) self.proxyObj =3D self.baseClass(**self.vo.to_python(commitDict= )) self._isDefaultObj =3D False =20 self.memento.clear() def rollBack(self): self.memento.clear() =20 class Default(object): pass class Entity(sqlobject.SQLObject): status =3D sqlobject.IntCol(default=3D0) createUser =3D sqlobject.StringCol(length=3D50) createDate =3D sqlobject.DateTimeCol() updateUser =3D sqlobject.StringCol(length=3D50) updateDate =3D sqlobject.DateTimeCol() idxStatus =3D sqlobject.DatabaseIndex('status') idxCreateDate =3D sqlobject.DatabaseIndex('createDate') idxUpdateDate =3D sqlobject.DatabaseIndex('updateDate') def GetDefaultProxy(self): return Proxy(self.DO(),self.VO(),self) GetDefaultProxy =3D classmethod(GetDefaultProxy) def GetInstanceProxy(self): return Proxy(self,self.VO(),self.__class__) class VO(formencode.Schema): status =3D formencode.All(validators.Int(not_empty=3DTrue), validators.OneOf([1,2,3,9])) createUser =3D validators.String(min=3D1,max=3D50) createDate =3D validators.DateConverter(not_empty=3DTrue) updateUser =3D validators.String(min=3D1,max=3D50) updateDate =3D validators.DateConverter(not_empty=3DTrue) class DO(Default): # all defaults should be strings to mimic comming from # the application def __init__(self): self.status =3D '1' # these are actually just stubs # most of this info will come from # other places in the framework self.createUser =3D 'djd' self.createDate =3D date.today().strftime('%m/%d/%Y') self.updateUser =3D 'djd' self.updateDate =3D date.today().strftime('%m/%d/%Y') class Person(Entity): firstName =3D sqlobject.StringCol(length=3D50) lastName =3D sqlobject.StringCol(length=3D50) =20 class VO(Entity.VO): firstName =3D validators.String(not_empty=3DTrue) lastName =3D validators.String(not_empty=3DTrue) class DO(Entity.DO): def __init__(self): Entity.DO.__init__(self) self.firstName =3D '' self.lastName =3D '' =20 def tests(): print 'reset the person table' Person.dropTable(ifExists=3DTrue) Person.createTable() print 'Get an empty proxied person ep then mess with it' ep =3D Person.GetDefaultProxy() print ep ep.firstName =3D 'Daria' ep.lastName =3D 'Driver' print ep.createUser print ep.createDate print ep.updateUser print ep.updateDate print ep.firstName print ep.lastName print ep._isDefaultObj try: ep.commit() except formencode.Invalid, err: for n,m in err.error_dict.items(): print n, m.msg,'\n','Previous Value',m.value,'\n' =20 print ep._isDefaultObj #this should fail with a validation error ep.createUser =3D '' try: ep.commit() except formencode.Invalid, err: for n,m in err.error_dict.items(): print n, m.msg,'\n','Previous Value',m.value,'\n' =20 if __name__ =3D=3D '__main__':tests() |