[TransWarp] TableDM proof of concept, request for comments
John Landahl
john at landahl.org
Thu Oct 9 15:36:10 EDT 2003
I've added a bit more functionality to the TableDM code seen in my
recent post, and thought it might be worth sharing with the rest of the
PEAK community. TableDM encapsulates all the basic functionality needed
to load, create, and update records in a SQL database, using mapping
classes to define how to move data between specific properties and
fields, enumerations, or other DMs.
The FieldMap class is used for simple property-to-field mappings, the
EnumerationMap class automates moving data into and out of PEAK
enumerations, and QueryMap and EntityMap provide mapping functionality
for QueryDMs and EntityDMs, respectively.
The Bulletins DM examples could be simplified using TableDM follows:
class UserDM(TableDM):
db = binding.bindTo(DATABASE)
defaultClass = User
fieldMap = (
FieldMap('loginId'),
FieldMap('fullName'),
FieldMap('password')
)
class BulletinDM(TableDM):
db = binding.bindTo(DATABASE)
CategoryDM = binding.bindTo(storage.DMFor(Category))
UserDM = binding.bindTo(storage.DMFor(User))
forCategory = binding.New(BulletinsForCategoryDM)
defaultClass = Bulletin
def fieldMap(self):
return (
FieldMap('id'),
EntityMap('category', self.CategoryDM)
FieldMap('fullText'),
EntityMap('postedBy', self.UserDM)
FieldMap('postedOn'),
EntityMap('editedBy', self.UserDM)
FieldMap('editedOn'),
# no equivalent for this yet, perhaps a ValueMap class?
# hidden = row.hidden <> 0,
)
fieldMap = binding.Make(fieldMap)
class CategoryDM(TableDM):
db = binding.bindTo(DATABASE)
BulletinDM = binding.bindTo(storage.DMFor(Bulletin))
bulletinsForCategory = binding.bindTo('BulletinDM/forCategory')
defaultClass = Category
def fieldMap(self):
return (
FieldMap('pathName'),
FieldMap('title'),
FieldMap('sortPosn'),
QueryMap('bulletins', self.bulletinsForCategory, 'pathName'),
EnumerationMap('sortBulletinsBy', SortBy),
FieldMap('postingTemplate'),
FieldMap('editingTemplate')
)
fieldMap = binding.Make(fieldMap)
See the attached tabledm.py for the code, and feel free to offer any
criticism or suggestions.
John Landahl
john at landahl.org
-------------- next part --------------
from peak.api import binding, storage, PropertyName
DATABASE = PropertyName('db')
class Mapping:
def __init__(self, property):
self.property = property
def writable(self, value):
return value
class FieldMap(Mapping):
def __init__(self, property, field=None):
Mapping.__init__(self, property)
if field:
self.field = field
else:
self.field = property
def mapFrom(self, row):
return self._field
class QueryMap(FieldMap):
def __init__(self, property, dm, field=None):
FieldMap.__init__(self, property, field)
self.dm = dm
def mapFrom(self, row):
return storage.QueryLink(self.dm[getattr(row, self.field)])
def writable(self, value):
return None
class EntityMap(QueryMap):
def writable(self, value):
return self.dm.oidFor(value)
class EnumerationMap(Mapping):
def __init__(self, property, enumeration, field=None):
Mapping.__init__(self, property)
self.enumeration = enumeration
if field:
self.field = field
else:
self.field = property
def mapFrom(self, row):
return self.enumeration(self.field)
def writable(self, value):
return hash(value)
class TableDM(storage.EntityDM):
log = binding.Obtain('logging.logger:storage.TableDM')
db = binding.Obtain(DATABASE)
table = binding.Require('Name of database table')
fieldMap = binding.Require('A list to map field names to property names')
keyField = binding.Require('The field name of the primary key for this table')
def _load(self, oid, ob):
row = ~self.db('select * from %s where %s=%%s' % (self.table, self.keyField), (oid,))
return self.stateFromRow(row)
def stateFromRow(self, row):
d = {}
for mapper in self.fieldMap:
d[property] = mapper.mapFrom(self, row)
return d
def _items(self, ob):
fields = []
values = []
placeholders = []
for mapper in self.fieldMap:
value = mapper.writable(getattr(ob, mapper.property, None))
if value is not None:
values.append(value)
fields.append(mapper.field)
placeholders.append('%s')
return fields, values, placeholders
def _save(self, ob):
fields, values, placeholders = self._items(ob)
assignments = ','.join(['%s = %%s' % field for field in fields])
sql = 'update %s set %s where %s = %%d' % (self.table, assignments, self.keyField)
self.db(sql, values + [ob._p_oid])
def _new(self, ob):
if ob.id:
ob._p_oid = ob.id
else:
ct, = ~self.db('select max(%s) from %s' % (self.keyField, self.table))
ct = int(ct or 0) + 1
ob._p_oid = ob.id = ct
fields, values, placeholders = self._items(ob)
sql = 'insert into %s (%s) values (%s)' % (
self.table,
','.join(fields),
','.join(placeholders)
)
self.db(sql, values)
return ob.id
def getAll(self):
return [
self.preloadState(getattr(row, self.keyField), self.stateFromRow(row))
for row in self.db('select * from %s' % self.table)
]
More information about the PEAK
mailing list