`0001import re
0002
0003
0004__all__ = ["Style", "MixedCaseUnderscoreStyle", "DefaultStyle",
0005 "MixedCaseStyle"]
0006
0007
0008class Style(object):
0009
0010 """
0011 The base Style class, and also the simplest implementation. No
0012 translation occurs -- column names and attribute names match,
0013 as do class names and table names (when using auto class or
0014 schema generation).
0015 """
0016
0017 def __init__(self, pythonAttrToDBColumn=None,
0018 dbColumnToPythonAttr=None,
0019 pythonClassToDBTable=None,
0020 dbTableToPythonClass=None,
0021 idForTable=None,
0022 longID=False):
0023 if pythonAttrToDBColumn:
0024 self.pythonAttrToDBColumn = lambda a, s=self: pythonAttrToDBColumn(s, a)
0026 if dbColumnToPythonAttr:
0027 self.dbColumnToPythonAttr = lambda a, s=self: dbColumnToPythonAttr(s, a)
0029 if pythonClassToDBTable:
0030 self.pythonClassToDBTable = lambda a, s=self: pythonClassToDBTable(s, a)
0032 if dbTableToPythonClass:
0033 self.dbTableToPythonClass = lambda a, s=self: dbTableToPythonClass(s, a)
0035 if idForTable:
0036 self.idForTable = lambda a, s=self: idForTable(s, a)
0037 self.longID = longID
0038
0039 def pythonAttrToDBColumn(self, attr):
0040 return attr
0041
0042 def dbColumnToPythonAttr(self, col):
0043 return col
0044
0045 def pythonClassToDBTable(self, className):
0046 return className
0047
0048 def dbTableToPythonClass(self, table):
0049 return table
0050
0051 def idForTable(self, table):
0052 if self.longID:
0053 return self.tableReference(table)
0054 else:
0055 return 'id'
0056
0057 def pythonClassToAttr(self, className):
0058 return lowerword(className)
0059
0060 def instanceAttrToIDAttr(self, attr):
0061 return attr + "ID"
0062
0063 def instanceIDAttrToAttr(self, attr):
0064 return attr[:-2]
0065
0066 def tableReference(self, table):
0067 return table + "_id"
0068
0069
0070class MixedCaseUnderscoreStyle(Style):
0071
0072 """
0073 This is the default style. Python attributes use mixedCase,
0074 while database columns use underscore_separated.
0075 """
0076
0077 def pythonAttrToDBColumn(self, attr):
0078 return mixedToUnder(attr)
0079
0080 def dbColumnToPythonAttr(self, col):
0081 return underToMixed(col)
0082
0083 def pythonClassToDBTable(self, className):
0084 return className[0].lower() + mixedToUnder(className[1:])
0086
0087 def dbTableToPythonClass(self, table):
0088 return table[0].upper() + underToMixed(table[1:])
0090
0091 def pythonClassToDBTableReference(self, className):
0092 return self.tableReference(self.pythonClassToDBTable(className))
0093
0094 def tableReference(self, table):
0095 return table + "_id"
0096
0097DefaultStyle = MixedCaseUnderscoreStyle
0098
0099
0100class MixedCaseStyle(Style):
0101
0102 """
0103 This style leaves columns as mixed-case, and uses long
0104 ID names (like ProductID instead of simply id).
0105 """
0106
0107 def pythonAttrToDBColumn(self, attr):
0108 return capword(attr)
0109
0110 def dbColumnToPythonAttr(self, col):
0111 return lowerword(col)
0112
0113 def dbTableToPythonClass(self, table):
0114 return capword(table)
0115
0116 def tableReference(self, table):
0117 return table + "ID"
0118
0119defaultStyle = DefaultStyle()
0120
0121
0122def getStyle(soClass, dbConnection=None):
0123 if dbConnection is None:
0124 if hasattr(soClass, '_connection'):
0125 dbConnection = soClass._connection
0126 if hasattr(soClass.sqlmeta, 'style') and soClass.sqlmeta.style:
0127 return soClass.sqlmeta.style
0128 elif dbConnection and dbConnection.style:
0129 return dbConnection.style
0130 else:
0131 return defaultStyle
0132
0133
0134############################################################
0135# Text utilities
0136############################################################
0137
0138
0139_mixedToUnderRE = re.compile(r'[A-Z]+')
0140
0141
0142def mixedToUnder(s):
0143 if s.endswith('ID'):
0144 return mixedToUnder(s[:-2] + "_id")
0145 trans = _mixedToUnderRE.sub(mixedToUnderSub, s)
0146 if trans.startswith('_'):
0147 trans = trans[1:]
0148 return trans
0149
0150
0151def mixedToUnderSub(match):
0152 m = match.group(0).lower()
0153 if len(m) > 1:
0154 return '_%s_%s' % (m[:-1], m[-1])
0155 else:
0156 return '_%s' % m
0157
0158
0159def capword(s):
0160 return s[0].upper() + s[1:]
0161
0162
0163def lowerword(s):
0164 return s[0].lower() + s[1:]
0165
0166
0167_underToMixedRE = re.compile('_.')
0168
0169
0170def underToMixed(name):
0171 if name.endswith('_id'):
0172 return underToMixed(name[:-3] + "ID")
0173 return _underToMixedRE.sub(lambda m: m.group(0)[1].upper(),
0174 name)`