0001"""
0002Col -- SQLObject columns
0003
0004Note that each column object is named BlahBlahCol, and these are used
0005in class definitions. But there's also a corresponding SOBlahBlahCol
0006object, which is used in SQLObject *classes*.
0007
0008An explanation: when a SQLObject subclass is created, the metaclass
0009looks through your class definition for any subclasses of Col. It
0010collects them together, and indexes them to do all the database stuff
0011you like, like the magic attributes and whatnot. It then asks the Col
0012object to create an SOCol object (usually a subclass, actually). The
0013SOCol object contains all the interesting logic, as well as a record
0014of the attribute name you used and the class it is bound to (set by
0015the metaclass).
0016
0017So, in summary: Col objects are what you define, but SOCol objects
0018are what gets used.
0019"""
0020
0021from array import array
0022from itertools import count
0023import re
0024import sys
0025import time
0026try:
0027 import cPickle as pickle
0028except ImportError:
0029 import pickle
0030import weakref
0031from formencode import compound, validators
0032from classregistry import findClass
0033
0034
0035import constraints as constrs
0036import sqlbuilder
0037from styles import capword
0038
0039NoDefault = sqlbuilder.NoDefault
0040
0041import datetime
0042datetime_available = True
0043
0044try:
0045 from mx import DateTime
0046except ImportError:
0047 try:
0048 import DateTime
0049 except ImportError:
0050 mxdatetime_available = False
0051 else:
0052 mxdatetime_available = True
0053else:
0054 mxdatetime_available = True
0055
0056DATETIME_IMPLEMENTATION = "datetime"
0057MXDATETIME_IMPLEMENTATION = "mxDateTime"
0058
0059if mxdatetime_available:
0060 if hasattr(DateTime, "Time"):
0061 DateTimeType = type(DateTime.now())
0062 TimeType = type(DateTime.Time())
0063 else:
0064 DateTimeType = type(DateTime.DateTime())
0065 TimeType = type(DateTime.DateTime.Time(DateTime.DateTime()))
0066
0067default_datetime_implementation = DATETIME_IMPLEMENTATION
0068
0069__all__ = ["datetime_available", "mxdatetime_available",
0070 "default_datetime_implementation", "DATETIME_IMPLEMENTATION"]
0071
0072if mxdatetime_available:
0073 __all__.append("MXDATETIME_IMPLEMENTATION")
0074
0075
0076creationOrder = count()
0077
0078
0079
0080
0081
0082
0083
0084class SOCol(object):
0085
0086 def __init__(self,
0087 name,
0088 soClass,
0089 creationOrder,
0090 dbName=None,
0091 default=NoDefault,
0092 defaultSQL=None,
0093 foreignKey=None,
0094 alternateID=False,
0095 alternateMethodName=None,
0096 constraints=None,
0097 notNull=NoDefault,
0098 notNone=NoDefault,
0099 unique=NoDefault,
0100 sqlType=None,
0101 columnDef=None,
0102 validator=None,
0103 validator2=None,
0104 immutable=False,
0105 cascade=None,
0106 lazy=False,
0107 noCache=False,
0108 forceDBName=False,
0109 title=None,
0110 tags=[],
0111 origName=None,
0112 dbEncoding=None,
0113 extra_vars=None):
0114
0115 super(SOCol, self).__init__()
0116
0117
0118
0119
0120
0121
0122 if not forceDBName:
0123 assert sqlbuilder.sqlIdentifier(name), 'Name must be SQL-safe (letters, numbers, underscores): %s (or use forceDBName=True)' % repr(name)
0125 assert name != 'id', 'The column name "id" is reserved for SQLObject use (and is implicitly created).'
0126 assert name, "You must provide a name for all columns"
0127
0128 self.columnDef = columnDef
0129 self.creationOrder = creationOrder
0130
0131 self.immutable = immutable
0132
0133
0134
0135
0136
0137
0138 if isinstance(cascade, str):
0139 assert cascade == 'null', (
0140 "The only string value allowed for cascade is 'null' (you gave: %r)" % cascade)
0141 self.cascade = cascade
0142
0143 if not isinstance(constraints, (list, tuple)):
0144 constraints = [constraints]
0145 self.constraints = self.autoConstraints() + constraints
0146
0147 self.notNone = False
0148 if notNull is not NoDefault:
0149 self.notNone = notNull
0150 assert notNone is NoDefault or (not notNone) == (not notNull), "The notNull and notNone arguments are aliases, and must not conflict. You gave notNull=%r, notNone=%r" % (notNull, notNone)
0153 elif notNone is not NoDefault:
0154 self.notNone = notNone
0155 if self.notNone:
0156 self.constraints = [constrs.notNull] + self.constraints
0157
0158 self.name = name
0159 self.soClass = soClass
0160 self._default = default
0161 self.defaultSQL = defaultSQL
0162 self.customSQLType = sqlType
0163
0164
0165 self.foreignKey = foreignKey
0166 if self.foreignKey:
0167 if origName is not None:
0168 idname = soClass.sqlmeta.style.instanceAttrToIDAttr(origName)
0169 else:
0170 idname = soClass.sqlmeta.style.instanceAttrToIDAttr(name)
0171 if self.name != idname:
0172 self.foreignName = self.name
0173 self.name = idname
0174 else:
0175 self.foreignName = soClass.sqlmeta.style.instanceIDAttrToAttr(self.name)
0176 else:
0177 self.foreignName = None
0178
0179
0180
0181
0182 if dbName is None:
0183 self.dbName = soClass.sqlmeta.style.pythonAttrToDBColumn(self.name)
0184 else:
0185 self.dbName = dbName
0186
0187
0188
0189 self.alternateID = alternateID
0190
0191 if unique is NoDefault:
0192 self.unique = alternateID
0193 else:
0194 self.unique = unique
0195 if self.unique and alternateMethodName is None:
0196 self.alternateMethodName = 'by' + capword(self.name)
0197 else:
0198 self.alternateMethodName = alternateMethodName
0199
0200 _validators = self.createValidators()
0201 if validator: _validators.append(validator)
0202 if validator2: _validators.insert(0, validator2)
0203 _vlen = len(_validators)
0204 if _vlen:
0205 for _validator in _validators:
0206 _validator.soCol=weakref.proxy(self)
0207 if _vlen == 0:
0208 self.validator = None
0209 elif _vlen == 1:
0210 self.validator = _validators[0]
0211 elif _vlen > 1:
0212 self.validator = compound.All.join(_validators[0], *_validators[1:])
0213 self.noCache = noCache
0214 self.lazy = lazy
0215
0216
0217 self.origName = origName or name
0218 self.title = title
0219 self.tags = tags
0220 self.dbEncoding = dbEncoding
0221
0222 if extra_vars:
0223 for name, value in extra_vars.items():
0224 setattr(self, name, value)
0225
0226 def _set_validator(self, value):
0227 self._validator = value
0228 if self._validator:
0229 self.to_python = self._validator.to_python
0230 self.from_python = self._validator.from_python
0231 else:
0232 self.to_python = None
0233 self.from_python = None
0234
0235 def _get_validator(self):
0236 return self._validator
0237
0238 validator = property(_get_validator, _set_validator)
0239
0240 def createValidators(self):
0241 """Create a list of validators for the column."""
0242 return []
0243
0244 def autoConstraints(self):
0245 return []
0246
0247 def _get_default(self):
0248
0249
0250 if self._default is NoDefault:
0251 return NoDefault
0252 elif hasattr(self._default, '__sqlrepr__'):
0253 return self._default
0254 elif callable(self._default):
0255 return self._default()
0256 else:
0257 return self._default
0258 default = property(_get_default, None, None)
0259
0260 def _get_joinName(self):
0261 return self.soClass.sqlmeta.style.instanceIDAttrToAttr(self.name)
0262 joinName = property(_get_joinName, None, None)
0263
0264 def __repr__(self):
0265 r = '<%s %s' % (self.__class__.__name__, self.name)
0266 if self.default is not NoDefault:
0267 r += ' default=%s' % repr(self.default)
0268 if self.foreignKey:
0269 r += ' connected to %s' % self.foreignKey
0270 if self.alternateID:
0271 r += ' alternate ID'
0272 if self.notNone:
0273 r += ' not null'
0274 return r + '>'
0275
0276 def createSQL(self):
0277 return ' '.join([self._sqlType()] + self._extraSQL())
0278
0279 def _extraSQL(self):
0280 result = []
0281 if self.notNone or self.alternateID:
0282 result.append('NOT NULL')
0283 if self.unique or self.alternateID:
0284 result.append('UNIQUE')
0285 if self.defaultSQL is not None:
0286 result.append("DEFAULT %s" % self.defaultSQL)
0287 return result
0288
0289 def _sqlType(self):
0290 if self.customSQLType is None:
0291 raise ValueError, ("Col %s (%s) cannot be used for automatic "
0292 "schema creation (too abstract)" %
0293 (self.name, self.__class__))
0294 else:
0295 return self.customSQLType
0296
0297 def _mysqlType(self):
0298 return self._sqlType()
0299
0300 def _postgresType(self):
0301 return self._sqlType()
0302
0303 def _sqliteType(self):
0304
0305
0306 try:
0307 return self._sqlType()
0308 except ValueError:
0309 return ''
0310
0311 def _sybaseType(self):
0312 return self._sqlType()
0313
0314 def _mssqlType(self):
0315 return self._sqlType()
0316
0317 def _firebirdType(self):
0318 return self._sqlType()
0319
0320 def _maxdbType(self):
0321 return self._sqlType()
0322
0323 def mysqlCreateSQL(self, connection=None):
0324 self.connection = connection
0325 return ' '.join([self.dbName, self._mysqlType()] + self._extraSQL())
0326
0327 def postgresCreateSQL(self):
0328 return ' '.join([self.dbName, self._postgresType()] + self._extraSQL())
0329
0330 def sqliteCreateSQL(self):
0331 return ' '.join([self.dbName, self._sqliteType()] + self._extraSQL())
0332
0333 def sybaseCreateSQL(self):
0334 return ' '.join([self.dbName, self._sybaseType()] + self._extraSQL())
0335
0336 def mssqlCreateSQL(self, connection=None):
0337 self.connection = connection
0338 return ' '.join([self.dbName, self._mssqlType()] + self._extraSQL())
0339
0340 def firebirdCreateSQL(self):
0341
0342
0343
0344 if not isinstance(self, SOEnumCol):
0345 return ' '.join([self.dbName, self._firebirdType()] + self._extraSQL())
0346 else:
0347 return ' '.join([self.dbName] + [self._firebirdType()[0]] + self._extraSQL() + [self._firebirdType()[1]])
0348
0349 def maxdbCreateSQL(self):
0350 return ' '.join([self.dbName, self._maxdbType()] + self._extraSQL())
0351
0352 def __get__(self, obj, type=None):
0353 if obj is None:
0354
0355 return self
0356 if obj.sqlmeta._obsolete:
0357 raise RuntimeError('The object <%s %s> is obsolete' % (
0358 obj.__class__.__name__, obj.id))
0359 if obj.sqlmeta.cacheColumns:
0360 columns = obj.sqlmeta._columnCache
0361 if columns is None:
0362 obj.sqlmeta.loadValues()
0363 try:
0364 return columns[name]
0365 except KeyError:
0366 return obj.sqlmeta.loadColumn(self)
0367 else:
0368 return obj.sqlmeta.loadColumn(self)
0369
0370 def __set__(self, obj, value):
0371 if self.immutable:
0372 raise AttributeError("The column %s.%s is immutable" %
0373 (obj.__class__.__name__,
0374 self.name))
0375 obj.sqlmeta.setColumn(self, value)
0376
0377 def __delete__(self, obj):
0378 raise AttributeError("I can't be deleted from %r" % obj)
0379
0380 def getDbEncoding(self, state, default='utf-8'):
0381 if self.dbEncoding:
0382 return self.dbEncoding
0383 dbEncoding = state.soObject.sqlmeta.dbEncoding
0384 if dbEncoding:
0385 return dbEncoding
0386 try:
0387 connection = state.connection or state.soObject._connection
0388 except AttributeError:
0389 dbEncoding = None
0390 else:
0391 dbEncoding = getattr(connection, "dbEncoding", None)
0392 if not dbEncoding:
0393 dbEncoding = default
0394 return dbEncoding
0395
0396
0397class Col(object):
0398
0399 baseClass = SOCol
0400
0401 def __init__(self, name=None, **kw):
0402 super(Col, self).__init__()
0403 self.__dict__['_name'] = name
0404 self.__dict__['_kw'] = kw
0405 self.__dict__['creationOrder'] = creationOrder.next()
0406 self.__dict__['_extra_vars'] = {}
0407
0408 def _set_name(self, value):
0409 assert self._name is None or self._name == value, (
0410 "You cannot change a name after it has already been set "
0411 "(from %s to %s)" % (self.name, value))
0412 self.__dict__['_name'] = value
0413
0414 def _get_name(self):
0415 return self._name
0416
0417 name = property(_get_name, _set_name)
0418
0419 def withClass(self, soClass):
0420 return self.baseClass(soClass=soClass, name=self._name,
0421 creationOrder=self.creationOrder,
0422 columnDef=self,
0423 extra_vars=self._extra_vars,
0424 **self._kw)
0425
0426 def __setattr__(self, var, value):
0427 if var == 'name':
0428 super(Col, self).__setattr__(var, value)
0429 return
0430 self._extra_vars[var] = value
0431
0432 def __repr__(self):
0433 return '<%s %s %s>' % (
0434 self.__class__.__name__, hex(abs(id(self)))[2:],
0435 self._name or '(unnamed)')
0436
0437
0438class SOValidator(validators.Validator):
0439 def getDbEncoding(self, state, default='utf-8'):
0440 try:
0441 return self.dbEncoding
0442 except AttributeError:
0443 return self.soCol.getDbEncoding(state, default=default)
0444
0445
0446class SOStringLikeCol(SOCol):
0447 """A common ancestor for SOStringCol and SOUnicodeCol"""
0448 def __init__(self, **kw):
0449 self.length = kw.pop('length', None)
0450 self.varchar = kw.pop('varchar', 'auto')
0451 self.char_binary = kw.pop('char_binary', None)
0452 if not self.length:
0453 assert self.varchar == 'auto' or not self.varchar, "Without a length strings are treated as TEXT, not varchar"
0455 self.varchar = False
0456 elif self.varchar == 'auto':
0457 self.varchar = True
0458
0459 super(SOStringLikeCol, self).__init__(**kw)
0460
0461 def autoConstraints(self):
0462 constraints = [constrs.isString]
0463 if self.length is not None:
0464 constraints += [constrs.MaxLength(self.length)]
0465 return constraints
0466
0467 def _sqlType(self):
0468 if self.customSQLType is not None:
0469 return self.customSQLType
0470 if not self.length:
0471 return 'TEXT'
0472 elif self.varchar:
0473 return 'VARCHAR(%i)' % self.length
0474 else:
0475 return 'CHAR(%i)' % self.length
0476
0477 def _check_case_sensitive(self, db):
0478 if self.char_binary:
0479 raise ValueError, "%s does not support binary character columns" % db
0480
0481 def _mysqlType(self):
0482 type = self._sqlType()
0483 if self.char_binary:
0484 type += " BINARY"
0485 return type
0486
0487 def _postgresType(self):
0488 self._check_case_sensitive("PostgreSQL")
0489 return super(SOStringLikeCol, self)._postgresType()
0490
0491 def _sqliteType(self):
0492 self._check_case_sensitive("SQLite")
0493 return super(SOStringLikeCol, self)._sqliteType()
0494
0495 def _sybaseType(self):
0496 self._check_case_sensitive("SYBASE")
0497 type = self._sqlType()
0498 if not self.notNone and not self.alternateID:
0499 type += ' NULL'
0500 return type
0501
0502 def _mssqlType(self):
0503 if self.customSQLType is not None:
0504 return self.customSQLType
0505 if not self.length:
0506 if self.connection and self.connection.can_use_max_types():
0507 type = 'VARCHAR(MAX)'
0508 else:
0509 type = 'VARCHAR(4000)'
0510 elif self.varchar:
0511 type = 'VARCHAR(%i)' % self.length
0512 else:
0513 type = 'CHAR(%i)' % self.length
0514 if not self.notNone and not self.alternateID:
0515 type += ' NULL'
0516 return type
0517
0518 def _firebirdType(self):
0519 self._check_case_sensitive("FireBird")
0520 if not self.length:
0521 return 'BLOB SUB_TYPE TEXT'
0522 else:
0523 return self._sqlType()
0524
0525 def _maxdbType(self):
0526 self._check_case_sensitive("SAP DB/MaxDB")
0527 if not self.length:
0528 return 'LONG ASCII'
0529 else:
0530 return self._sqlType()
0531
0532
0533class StringValidator(SOValidator):
0534
0535 def to_python(self, value, state):
0536 if value is None:
0537 return None
0538 try:
0539 connection = state.connection or state.soObject._connection
0540 binaryType = connection._binaryType
0541 except AttributeError:
0542 binaryType = type(None)
0543 dbEncoding = self.getDbEncoding(state, default='ascii')
0544 if isinstance(value, unicode):
0545 return value.encode(dbEncoding)
0546 if self.dataType and isinstance(value, self.dataType):
0547 return value
0548 if isinstance(value, (str, buffer, binaryType, sqlbuilder.SQLExpression)):
0549 return value
0550 if hasattr(value, '__unicode__'):
0551 return unicode(value).encode(dbEncoding)
0552 raise validators.Invalid("expected a str in the StringCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
0554
0555 from_python = to_python
0556
0557class SOStringCol(SOStringLikeCol):
0558
0559 def createValidators(self, dataType=None):
0560 return [StringValidator(name=self.name, dataType=dataType)] + super(SOStringCol, self).createValidators()
0562
0563class StringCol(Col):
0564 baseClass = SOStringCol
0565
0566
0567class NQuoted(sqlbuilder.SQLExpression):
0568 def __init__(self, value):
0569 assert isinstance(value, unicode)
0570 self.value = value
0571 def __hash__(self):
0572 return hash(self.value)
0573 def __sqlrepr__(self, db):
0574 assert db == 'mssql'
0575 return "N" + sqlbuilder.sqlrepr(self.value, db)
0576
0577class UnicodeStringValidator(SOValidator):
0578
0579 def to_python(self, value, state):
0580 if value is None:
0581 return None
0582 if isinstance(value, (unicode, sqlbuilder.SQLExpression)):
0583 return value
0584 if isinstance(value, str):
0585 return unicode(value, self.getDbEncoding(state))
0586 if isinstance(value, array):
0587 return unicode(value.tostring(), self.getDbEncoding(state))
0588 if hasattr(value, '__unicode__'):
0589 return unicode(value)
0590 raise validators.Invalid("expected a str or a unicode in the UnicodeCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
0592
0593 def from_python(self, value, state):
0594 if value is None:
0595 return None
0596 if isinstance(value, (str, sqlbuilder.SQLExpression)):
0597 return value
0598 if isinstance(value, unicode):
0599 try:
0600 connection = state.connection or state.soObject._connection
0601 except AttributeError:
0602 pass
0603 else:
0604 if connection.dbName == 'mssql':
0605 return NQuoted(value)
0606 return value.encode(self.getDbEncoding(state))
0607 if hasattr(value, '__unicode__'):
0608 return unicode(value).encode(self.getDbEncoding(state))
0609 raise validators.Invalid("expected a str or a unicode in the UnicodeCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
0611
0612class SOUnicodeCol(SOStringLikeCol):
0613 def _mssqlType(self):
0614 if self.customSQLType is not None:
0615 return self.customSQLType
0616 return 'N' + super(SOUnicodeCol, self)._mssqlType()
0617
0618 def createValidators(self):
0619 return [UnicodeStringValidator(name=self.name)] + super(SOUnicodeCol, self).createValidators()
0621
0622class UnicodeCol(Col):
0623 baseClass = SOUnicodeCol
0624
0625
0626class IntValidator(SOValidator):
0627
0628 def to_python(self, value, state):
0629 if value is None:
0630 return None
0631 if isinstance(value, (int, long, sqlbuilder.SQLExpression)):
0632 return value
0633 for converter, attr_name in (int, '__int__'), (long, '__long__'):
0634 if hasattr(value, attr_name):
0635 try:
0636 return converter(value)
0637 except:
0638 break
0639 raise validators.Invalid("expected an int in the IntCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
0641
0642 from_python = to_python
0643
0644class SOIntCol(SOCol):
0645
0646 def __init__(self, **kw):
0647 self.length = kw.pop('length', None)
0648 self.unsigned = bool(kw.pop('unsigned', None))
0649 self.zerofill = bool(kw.pop('zerofill', None))
0650 SOCol.__init__(self, **kw)
0651
0652 def autoConstraints(self):
0653 return [constrs.isInt]
0654
0655 def createValidators(self):
0656 return [IntValidator(name=self.name)] + super(SOIntCol, self).createValidators()
0658
0659 def addSQLAttrs(self, str):
0660 _ret = str
0661 if str is None or len(str) < 1:
0662 return None
0663
0664 if self.length >= 1:
0665 _ret = "%s(%d)" % (_ret, self.length)
0666 if self.unsigned:
0667 _ret = _ret + " UNSIGNED"
0668 if self.zerofill:
0669 _ret = _ret + " ZEROFILL"
0670 return _ret
0671
0672 def _sqlType(self):
0673 return self.addSQLAttrs("INT")
0674
0675class IntCol(Col):
0676 baseClass = SOIntCol
0677
0678class SOTinyIntCol(SOIntCol):
0679 def _sqlType(self):
0680 return self.addSQLAttrs("TINYINT")
0681
0682class TinyIntCol(Col):
0683 baseClass = SOTinyIntCol
0684
0685class SOSmallIntCol(SOIntCol):
0686 def _sqlType(self):
0687 return self.addSQLAttrs("SMALLINT")
0688
0689class SmallIntCol(Col):
0690 baseClass = SOSmallIntCol
0691
0692class SOMediumIntCol(SOIntCol):
0693 def _sqlType(self):
0694 return self.addSQLAttrs("MEDIUMINT")
0695
0696class MediumIntCol(Col):
0697 baseClass = SOMediumIntCol
0698
0699class SOBigIntCol(SOIntCol):
0700 def _sqlType(self):
0701 return self.addSQLAttrs("BIGINT")
0702
0703class BigIntCol(Col):
0704 baseClass = SOBigIntCol
0705
0706
0707class BoolValidator(SOValidator):
0708
0709 def to_python(self, value, state):
0710 if value is None:
0711 return None
0712 if isinstance(value, (bool, sqlbuilder.SQLExpression)):
0713 return value
0714 if isinstance(value, (int, long)) or hasattr(value, '__nonzero__'):
0715 return bool(value)
0716 raise validators.Invalid("expected a bool or an int in the BoolCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
0718
0719 from_python = to_python
0720
0721class SOBoolCol(SOCol):
0722 def autoConstraints(self):
0723 return [constrs.isBool]
0724
0725 def createValidators(self):
0726 return [BoolValidator(name=self.name)] + super(SOBoolCol, self).createValidators()
0728
0729 def _postgresType(self):
0730 return 'BOOL'
0731
0732 def _mysqlType(self):
0733 return "BOOL"
0734
0735 def _sybaseType(self):
0736 return "BIT"
0737
0738 def _mssqlType(self):
0739 return "BIT"
0740
0741 def _firebirdType(self):
0742 return 'INT'
0743
0744 def _maxdbType(self):
0745 return "BOOLEAN"
0746
0747 def _sqliteType(self):
0748 return "BOOLEAN"
0749
0750class BoolCol(Col):
0751 baseClass = SOBoolCol
0752
0753
0754class FloatValidator(SOValidator):
0755
0756 def to_python(self, value, state):
0757 if value is None:
0758 return None
0759 if isinstance(value, (float, int, long, sqlbuilder.SQLExpression)):
0760 return value
0761 for converter, attr_name in (float, '__float__'), (int, '__int__'), (long, '__long__'):
0762 if hasattr(value, attr_name):
0763 try:
0764 return converter(value)
0765 except:
0766 break
0767 raise validators.Invalid("expected a float in the FloatCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
0769
0770 from_python = to_python
0771
0772class SOFloatCol(SOCol):
0773
0774
0775 def autoConstraints(self):
0776 return [constrs.isFloat]
0777
0778 def createValidators(self):
0779 return [FloatValidator(name=self.name)] + super(SOFloatCol, self).createValidators()
0781
0782 def _sqlType(self):
0783 return 'FLOAT'
0784
0785 def _mysqlType(self):
0786 return "DOUBLE PRECISION"
0787
0788class FloatCol(Col):
0789 baseClass = SOFloatCol
0790
0791
0792class SOKeyCol(SOCol):
0793 key_type = {int: "INT", str: "TEXT"}
0794
0795
0796
0797
0798 def __init__(self, **kw):
0799 self.refColumn = kw.pop('refColumn', None)
0800 super(SOKeyCol, self).__init__(**kw)
0801
0802 def _sqlType(self):
0803 return self.key_type[self.soClass.sqlmeta.idType]
0804
0805 def _sybaseType(self):
0806 key_type = {int: "NUMERIC(18,0) NULL", str: "TEXT"}
0807 return key_type[self.soClass.sqlmeta.idType]
0808
0809 def _mssqlType(self):
0810 key_type = {int: "INT NULL", str: "TEXT"}
0811 return key_type[self.soClass.sqlmeta.idType]
0812
0813class KeyCol(Col):
0814
0815 baseClass = SOKeyCol
0816
0817class SOForeignKey(SOKeyCol):
0818
0819 def __init__(self, **kw):
0820 foreignKey = kw['foreignKey']
0821 style = kw['soClass'].sqlmeta.style
0822 if kw.get('name'):
0823 kw['origName'] = kw['name']
0824 kw['name'] = style.instanceAttrToIDAttr(kw['name'])
0825 else:
0826 kw['name'] = style.instanceAttrToIDAttr(style.pythonClassToAttr(foreignKey))
0827 super(SOForeignKey, self).__init__(**kw)
0828
0829 def sqliteCreateSQL(self):
0830 sql = SOKeyCol.sqliteCreateSQL(self)
0831 other = findClass(self.foreignKey, self.soClass.sqlmeta.registry)
0832 tName = other.sqlmeta.table
0833 idName = self.refColumn or other.sqlmeta.idName
0834 if self.cascade is not None:
0835 if self.cascade == 'null':
0836 action = 'ON DELETE SET NULL'
0837 elif self.cascade:
0838 action = 'ON DELETE CASCADE'
0839 else:
0840 action = 'ON DELETE RESTRICT'
0841 else:
0842 action = ''
0843 constraint = ('CONSTRAINT %(colName)s_exists '
0844
0845 'REFERENCES %(tName)s(%(idName)s) '
0846 '%(action)s' %
0847 {'tName': tName,
0848 'colName': self.dbName,
0849 'idName': idName,
0850 'action': action})
0851 sql = ' '.join([sql, constraint])
0852 return sql
0853
0854 def postgresCreateSQL(self):
0855 sql = SOKeyCol.postgresCreateSQL(self)
0856 return sql
0857
0858 def postgresCreateReferenceConstraint(self):
0859 sTName = self.soClass.sqlmeta.table
0860 other = findClass(self.foreignKey, self.soClass.sqlmeta.registry)
0861 tName = other.sqlmeta.table
0862 idName = self.refColumn or other.sqlmeta.idName
0863 if self.cascade is not None:
0864 if self.cascade == 'null':
0865 action = 'ON DELETE SET NULL'
0866 elif self.cascade:
0867 action = 'ON DELETE CASCADE'
0868 else:
0869 action = 'ON DELETE RESTRICT'
0870 else:
0871 action = ''
0872 constraint = ('ALTER TABLE %(sTName)s ADD CONSTRAINT %(colName)s_exists '
0873 'FOREIGN KEY (%(colName)s) '
0874 'REFERENCES %(tName)s (%(idName)s) '
0875 '%(action)s' %
0876 {'tName': tName,
0877 'colName': self.dbName,
0878 'idName': idName,
0879 'action': action,
0880 'sTName': sTName})
0881 return constraint
0882
0883 def mysqlCreateReferenceConstraint(self):
0884 sTName = self.soClass.sqlmeta.table
0885 sTLocalName = sTName.split('.')[-1]
0886 other = findClass(self.foreignKey, self.soClass.sqlmeta.registry)
0887 tName = other.sqlmeta.table
0888 idName = self.refColumn or other.sqlmeta.idName
0889 if self.cascade is not None:
0890 if self.cascade == 'null':
0891 action = 'ON DELETE SET NULL'
0892 elif self.cascade:
0893 action = 'ON DELETE CASCADE'
0894 else:
0895 action = 'ON DELETE RESTRICT'
0896 else:
0897 action = ''
0898 constraint = ('ALTER TABLE %(sTName)s ADD CONSTRAINT %(sTLocalName)s_%(colName)s_exists '
0899 'FOREIGN KEY (%(colName)s) '
0900 'REFERENCES %(tName)s (%(idName)s) '
0901 '%(action)s' %
0902 {'tName': tName,
0903 'colName': self.dbName,
0904 'idName': idName,
0905 'action': action,
0906 'sTName': sTName,
0907 'sTLocalName': sTLocalName})
0908 return constraint
0909
0910 def mysqlCreateSQL(self, connection=None):
0911 return SOKeyCol.mysqlCreateSQL(self, connection)
0912
0913 def sybaseCreateSQL(self):
0914 sql = SOKeyCol.sybaseCreateSQL(self)
0915 other = findClass(self.foreignKey, self.soClass.sqlmeta.registry)
0916 tName = other.sqlmeta.table
0917 idName = self.refColumn or other.sqlmeta.idName
0918 reference = ('REFERENCES %(tName)s(%(idName)s) ' %
0919 {'tName':tName,
0920 'idName':idName})
0921 sql = ' '.join([sql, reference])
0922 return sql
0923
0924 def sybaseCreateReferenceConstraint(self):
0925
0926 return None
0927
0928 def mssqlCreateSQL(self, connection=None):
0929 sql = SOKeyCol.mssqlCreateSQL(self, connection)
0930 other = findClass(self.foreignKey, self.soClass.sqlmeta.registry)
0931 tName = other.sqlmeta.table
0932 idName = self.refColumn or other.sqlmeta.idName
0933 reference = ('REFERENCES %(tName)s(%(idName)s) ' %
0934 {'tName':tName,
0935 'idName':idName})
0936 sql = ' '.join([sql, reference])
0937 return sql
0938
0939 def mssqlCreateReferenceConstraint(self):
0940
0941 return None
0942
0943 def maxdbCreateSQL(self):
0944 other = findClass(self.foreignKey, self.soClass.sqlmeta.registry)
0945 fidName = self.dbName
0946
0947 sql = ' '.join([fidName, self._maxdbType()])
0948 tName = other.sqlmeta.table
0949 idName = self.refColumn or other.sqlmeta.idName
0950 sql=sql + ',' + '\n'
0951 sql=sql + 'FOREIGN KEY (%s) REFERENCES %s(%s)'%(fidName,tName,idName)
0952 return sql
0953
0954 def maxdbCreateReferenceConstraint(self):
0955
0956 return None
0957
0958class ForeignKey(KeyCol):
0959
0960 baseClass = SOForeignKey
0961
0962 def __init__(self, foreignKey=None, **kw):
0963 super(ForeignKey, self).__init__(foreignKey=foreignKey, **kw)
0964
0965
0966class EnumValidator(SOValidator):
0967
0968 def to_python(self, value, state):
0969 if value in self.enumValues:
0970 if isinstance(value, unicode):
0971 dbEncoding = self.getDbEncoding(state)
0972 value = value.encode(dbEncoding)
0973 return value
0974 elif not self.notNone and value is None:
0975 return None
0976 raise validators.Invalid("expected a member of %r in the EnumCol '%s', got %r instead" % (self.enumValues, self.name, value), value, state)
0978
0979 from_python = to_python
0980
0981class SOEnumCol(SOCol):
0982
0983 def __init__(self, **kw):
0984 self.enumValues = kw.pop('enumValues', None)
0985 assert self.enumValues is not None, 'You must provide an enumValues keyword argument'
0987 super(SOEnumCol, self).__init__(**kw)
0988
0989 def autoConstraints(self):
0990 return [constrs.isString, constrs.InList(self.enumValues)]
0991
0992 def createValidators(self):
0993 return [EnumValidator(name=self.name, enumValues=self.enumValues,
0994 notNone=self.notNone)] + super(SOEnumCol, self).createValidators()
0996
0997 def _mysqlType(self):
0998
0999
1000 if None in self.enumValues:
1001 return "ENUM(%s)" % ', '.join([sqlbuilder.sqlrepr(v, 'mysql') for v in self.enumValues if v is not None])
1002 else:
1003 return "ENUM(%s) NOT NULL" % ', '.join([sqlbuilder.sqlrepr(v, 'mysql') for v in self.enumValues])
1004
1005 def _postgresType(self):
1006 length = max(map(self._getlength, self.enumValues))
1007 enumValues = ', '.join([sqlbuilder.sqlrepr(v, 'postgres') for v in self.enumValues])
1008 checkConstraint = "CHECK (%s in (%s))" % (self.dbName, enumValues)
1009 return "VARCHAR(%i) %s" % (length, checkConstraint)
1010
1011 _sqliteType = _postgresType
1012
1013 def _sybaseType(self):
1014 return self._postgresType()
1015
1016 def _mssqlType(self):
1017 return self._postgresType()
1018
1019 def _firebirdType(self):
1020 length = max(map(self._getlength, self.enumValues))
1021 enumValues = ', '.join([sqlbuilder.sqlrepr(v, 'firebird') for v in self.enumValues])
1022 checkConstraint = "CHECK (%s in (%s))" % (self.dbName, enumValues)
1023
1024 return "VARCHAR(%i)" % (length), checkConstraint
1025
1026 def _maxdbType(self):
1027 raise TypeError("Enum type is not supported on MAX DB")
1028
1029 def _getlength(self, obj):
1030 """
1031 None counts as 0; everything else uses len()
1032 """
1033 if obj is None:
1034 return 0
1035 else:
1036 return len(obj)
1037
1038class EnumCol(Col):
1039 baseClass = SOEnumCol
1040
1041
1042class SetValidator(SOValidator):
1043 """
1044 Translates Python tuples into SQL comma-delimited SET strings.
1045 """
1046
1047 def to_python(self, value, state):
1048 if isinstance(value, str):
1049 return tuple(value.split(","))
1050 raise validators.Invalid("expected a string in the SetCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1052
1053 def from_python(self, value, state):
1054 if isinstance(value, basestring):
1055 value = (value,)
1056 try:
1057 return ",".join(value)
1058 except:
1059 raise validators.Invalid("expected a string or a sequence of stringsin the SetCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1061
1062class SOSetCol(SOCol):
1063 def __init__(self, **kw):
1064 self.setValues = kw.pop('setValues', None)
1065 assert self.setValues is not None, 'You must provide a setValues keyword argument'
1067 super(SOSetCol, self).__init__(**kw)
1068
1069 def autoConstraints(self):
1070 return [constrs.isString, constrs.InList(self.setValues)]
1071
1072 def createValidators(self):
1073 return [SetValidator(name=self.name, setValues=self.setValues)] + super(SOSetCol, self).createValidators()
1075
1076 def _mysqlType(self):
1077 return "SET(%s)" % ', '.join([sqlbuilder.sqlrepr(v, 'mysql') for v in self.setValues])
1078
1079class SetCol(Col):
1080 baseClass = SOSetCol
1081
1082
1083class DateTimeValidator(validators.DateValidator):
1084 def to_python(self, value, state):
1085 if value is None:
1086 return None
1087 if isinstance(value, (datetime.datetime, datetime.date, datetime.time, sqlbuilder.SQLExpression)):
1088 return value
1089 if mxdatetime_available:
1090 if isinstance(value, DateTimeType):
1091
1092 if (self.format.find("%H") >= 0) or (self.format.find("%T")) >= 0:
1093 return datetime.datetime(value.year, value.month, value.day,
1094 value.hour, value.minute, int(value.second))
1095 else:
1096 return datetime.date(value.year, value.month, value.day)
1097 elif isinstance(value, TimeType):
1098
1099 if self.format.find("%d") >= 0:
1100 return datetime.timedelta(seconds=value.seconds)
1101 else:
1102 return datetime.time(value.hour, value.minute, int(value.second))
1103 try:
1104 if self.format.find(".%f") >= 0:
1105 if '.' in value:
1106 _value = value.split('.')
1107 microseconds = _value[-1]
1108 _l = len(microseconds)
1109 if _l < 6:
1110 _value[-1] = microseconds + '0'*(6 - _l)
1111 elif _l > 6:
1112 _value[-1] = microseconds[:6]
1113 if _l != 6:
1114 value = '.'.join(_value)
1115 else:
1116 value += '.0'
1117 return datetime.datetime.strptime(value, self.format)
1118 except:
1119 raise validators.Invalid("expected a date/time string of the '%s' format in the DateTimeCol '%s', got %s %r instead" % (self.format, self.name, type(value), value), value, state)
1121
1122 def from_python(self, value, state):
1123 if value is None:
1124 return None
1125 if isinstance(value, (datetime.datetime, datetime.date, datetime.time, sqlbuilder.SQLExpression)):
1126 return value
1127 if hasattr(value, "strftime"):
1128 return value.strftime(self.format)
1129 raise validators.Invalid("expected a datetime in the DateTimeCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1131
1132if mxdatetime_available:
1133 class MXDateTimeValidator(validators.DateValidator):
1134 def to_python(self, value, state):
1135 if value is None:
1136 return None
1137 if isinstance(value, (DateTimeType, TimeType, sqlbuilder.SQLExpression)):
1138 return value
1139 if isinstance(value, datetime.datetime):
1140 return DateTime.DateTime(value.year, value.month, value.day,
1141 value.hour, value.minute, value.second)
1142 elif isinstance(value, datetime.date):
1143 return DateTime.Date(value.year, value.month, value.day)
1144 elif isinstance(value, datetime.time):
1145 return DateTime.Time(value.hour, value.minute, value.second)
1146 try:
1147 if self.format.find(".%f") >= 0:
1148 if '.' in value:
1149 _value = value.split('.')
1150 microseconds = _value[-1]
1151 _l = len(microseconds)
1152 if _l < 6:
1153 _value[-1] = microseconds + '0'*(6 - _l)
1154 elif _l > 6:
1155 _value[-1] = microseconds[:6]
1156 if _l != 6:
1157 value = '.'.join(_value)
1158 else:
1159 value += '.0'
1160 value = datetime.datetime.strptime(value, self.format)
1161 return DateTime.DateTime(value.year, value.month, value.day,
1162 value.hour, value.minute, value.second)
1163 except:
1164 raise validators.Invalid("expected a date/time string of the '%s' format in the DateTimeCol '%s', got %s %r instead" % (self.format, self.name, type(value), value), value, state)
1166
1167 def from_python(self, value, state):
1168 if value is None:
1169 return None
1170 if isinstance(value, (DateTimeType, TimeType, sqlbuilder.SQLExpression)):
1171 return value
1172 if hasattr(value, "strftime"):
1173 return value.strftime(self.format)
1174 raise validators.Invalid("expected a mxDateTime in the DateTimeCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1176
1177class SODateTimeCol(SOCol):
1178 datetimeFormat = '%Y-%m-%d %H:%M:%S.%f'
1179
1180 def __init__(self, **kw):
1181 datetimeFormat = kw.pop('datetimeFormat', None)
1182 if datetimeFormat:
1183 self.datetimeFormat = datetimeFormat
1184 super(SODateTimeCol, self).__init__(**kw)
1185
1186 def createValidators(self):
1187 _validators = super(SODateTimeCol, self).createValidators()
1188 if default_datetime_implementation == DATETIME_IMPLEMENTATION:
1189 validatorClass = DateTimeValidator
1190 elif default_datetime_implementation == MXDATETIME_IMPLEMENTATION:
1191 validatorClass = MXDateTimeValidator
1192 if default_datetime_implementation:
1193 _validators.insert(0, validatorClass(name=self.name, format=self.datetimeFormat))
1194 return _validators
1195
1196 def _mysqlType(self):
1197 if self.connection and self.connection.can_use_microseconds():
1198 return 'DATETIME(6)'
1199 else:
1200 return 'DATETIME'
1201
1202 def _postgresType(self):
1203 return 'TIMESTAMP'
1204
1205 def _sybaseType(self):
1206 return 'DATETIME'
1207
1208 def _mssqlType(self):
1209 return 'DATETIME'
1210
1211 def _sqliteType(self):
1212 return 'TIMESTAMP'
1213
1214 def _firebirdType(self):
1215 return 'TIMESTAMP'
1216
1217 def _maxdbType(self):
1218 return 'TIMESTAMP'
1219
1220class DateTimeCol(Col):
1221 baseClass = SODateTimeCol
1222 @staticmethod
1223 def now():
1224 if default_datetime_implementation == DATETIME_IMPLEMENTATION:
1225 return datetime.datetime.now()
1226 elif default_datetime_implementation == MXDATETIME_IMPLEMENTATION:
1227 return DateTime.now()
1228 else:
1229 assert 0, ("No datetime implementation available "
1230 "(DATETIME_IMPLEMENTATION=%r)"
1231 % DATETIME_IMPLEMENTATION)
1232
1233
1234class DateValidator(DateTimeValidator):
1235 def to_python(self, value, state):
1236 if isinstance(value, datetime.datetime):
1237 value = value.date()
1238 if isinstance(value, (datetime.date, sqlbuilder.SQLExpression)):
1239 return value
1240 value = super(DateValidator, self).to_python(value, state)
1241 if isinstance(value, datetime.datetime):
1242 value = value.date()
1243 return value
1244
1245 from_python = to_python
1246
1247class SODateCol(SOCol):
1248 dateFormat = '%Y-%m-%d'
1249
1250 def __init__(self, **kw):
1251 dateFormat = kw.pop('dateFormat', None)
1252 if dateFormat: self.dateFormat = dateFormat
1253 super(SODateCol, self).__init__(**kw)
1254
1255 def createValidators(self):
1256 """Create a validator for the column. Can be overriden in descendants."""
1257 _validators = super(SODateCol, self).createValidators()
1258 if default_datetime_implementation == DATETIME_IMPLEMENTATION:
1259 validatorClass = DateValidator
1260 elif default_datetime_implementation == MXDATETIME_IMPLEMENTATION:
1261 validatorClass = MXDateTimeValidator
1262 if default_datetime_implementation:
1263 _validators.insert(0, validatorClass(name=self.name, format=self.dateFormat))
1264 return _validators
1265
1266 def _mysqlType(self):
1267 return 'DATE'
1268
1269 def _postgresType(self):
1270 return 'DATE'
1271
1272 def _sybaseType(self):
1273 return self._postgresType()
1274
1275 def _mssqlType(self):
1276 """
1277 SQL Server doesn't have a DATE data type, to emulate we use a vc(10)
1278 """
1279 return 'VARCHAR(10)'
1280
1281 def _firebirdType(self):
1282 return 'DATE'
1283
1284 def _maxdbType(self):
1285 return 'DATE'
1286
1287 def _sqliteType(self):
1288 return 'DATE'
1289
1290class DateCol(Col):
1291 baseClass = SODateCol
1292
1293
1294class TimeValidator(DateTimeValidator):
1295 def to_python(self, value, state):
1296 if isinstance(value, (datetime.time, sqlbuilder.SQLExpression)):
1297 return value
1298 if isinstance(value, datetime.timedelta):
1299 if value.days:
1300 raise validators.Invalid(
1301 "the value for the TimeCol '%s' must has days=0, it has days=%d" %
1302 (self.name, value.days), value, state)
1303 return datetime.time(*time.gmtime(value.seconds)[3:6])
1304 value = super(TimeValidator, self).to_python(value, state)
1305 if isinstance(value, datetime.datetime):
1306 value = value.time()
1307 return value
1308
1309 from_python = to_python
1310
1311class SOTimeCol(SOCol):
1312 timeFormat = '%H:%M:%S.%f'
1313
1314 def __init__(self, **kw):
1315 timeFormat = kw.pop('timeFormat', None)
1316 if timeFormat:
1317 self.timeFormat = timeFormat
1318 super(SOTimeCol, self).__init__(**kw)
1319
1320 def createValidators(self):
1321 _validators = super(SOTimeCol, self).createValidators()
1322 if default_datetime_implementation == DATETIME_IMPLEMENTATION:
1323 validatorClass = TimeValidator
1324 elif default_datetime_implementation == MXDATETIME_IMPLEMENTATION:
1325 validatorClass = MXDateTimeValidator
1326 if default_datetime_implementation:
1327 _validators.insert(0, validatorClass(name=self.name, format=self.timeFormat))
1328 return _validators
1329
1330 def _mysqlType(self):
1331 if self.connection and self.connection.can_use_microseconds():
1332 return 'TIME(6)'
1333 else:
1334 return 'TIME'
1335
1336 def _postgresType(self):
1337 return 'TIME'
1338
1339 def _sybaseType(self):
1340 return 'TIME'
1341
1342 def _sqliteType(self):
1343 return 'TIME'
1344
1345 def _firebirdType(self):
1346 return 'TIME'
1347
1348 def _maxdbType(self):
1349 return 'TIME'
1350
1351class TimeCol(Col):
1352 baseClass = SOTimeCol
1353
1354
1355class SOTimestampCol(SODateTimeCol):
1356 """
1357 Necessary to support MySQL's use of TIMESTAMP versus DATETIME types
1358 """
1359
1360 def __init__(self, **kw):
1361 if 'default' not in kw:
1362 kw['default'] = None
1363 SOCol.__init__(self, **kw)
1364
1365 def _mysqlType(self):
1366 if self.connection and self.connection.can_use_microseconds():
1367 return 'TIMESTAMP(6)'
1368 else:
1369 return 'TIMESTAMP'
1370
1371class TimestampCol(Col):
1372 baseClass = SOTimestampCol
1373
1374
1375class TimedeltaValidator(SOValidator):
1376 def to_python(self, value, state):
1377 return value
1378
1379 from_python = to_python
1380
1381class SOTimedeltaCol(SOCol):
1382 def _postgresType(self):
1383 return 'INTERVAL'
1384
1385 def createValidators(self):
1386 return [TimedeltaValidator(name=self.name)] + super(SOTimedeltaCol, self).createValidators()
1388
1389class TimedeltaCol(Col):
1390 baseClass = SOTimedeltaCol
1391
1392
1393from decimal import Decimal
1394
1395class DecimalValidator(SOValidator):
1396 def to_python(self, value, state):
1397 if value is None:
1398 return None
1399 if isinstance(value, (int, long, Decimal, sqlbuilder.SQLExpression)):
1400 return value
1401 if isinstance(value, float):
1402 value = str(value)
1403 try:
1404 connection = state.connection or state.soObject._connection
1405 except AttributeError:
1406 pass
1407 else:
1408 if hasattr(connection, "decimalSeparator"):
1409 value = value.replace(connection.decimalSeparator, ".")
1410 try:
1411 return Decimal(value)
1412 except:
1413 raise validators.Invalid("expected a Decimal in the DecimalCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1415
1416 def from_python(self, value, state):
1417 if value is None:
1418 return None
1419 if isinstance(value, float):
1420 value = str(value)
1421 if isinstance(value, basestring):
1422 try:
1423 connection = state.connection or state.soObject._connection
1424 except AttributeError:
1425 pass
1426 else:
1427 if hasattr(connection, "decimalSeparator"):
1428 value = value.replace(connection.decimalSeparator, ".")
1429 try:
1430 return Decimal(value)
1431 except:
1432 raise validators.Invalid("can not parse Decimal value '%s' in the DecimalCol from '%s'" %
1433 (value, getattr(state, 'soObject', '(unknown)')), value, state)
1434 if isinstance(value, (int, long, Decimal, sqlbuilder.SQLExpression)):
1435 return value
1436 raise validators.Invalid("expected a Decimal in the DecimalCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1438
1439class SODecimalCol(SOCol):
1440
1441 def __init__(self, **kw):
1442 self.size = kw.pop('size', NoDefault)
1443 assert self.size is not NoDefault, "You must give a size argument"
1445 self.precision = kw.pop('precision', NoDefault)
1446 assert self.precision is not NoDefault, "You must give a precision argument"
1448 super(SODecimalCol, self).__init__(**kw)
1449
1450 def _sqlType(self):
1451 return 'DECIMAL(%i, %i)' % (self.size, self.precision)
1452
1453 def createValidators(self):
1454 return [DecimalValidator(name=self.name)] + super(SODecimalCol, self).createValidators()
1456
1457class DecimalCol(Col):
1458 baseClass = SODecimalCol
1459
1460class SOCurrencyCol(SODecimalCol):
1461
1462 def __init__(self, **kw):
1463 pushKey(kw, 'size', 10)
1464 pushKey(kw, 'precision', 2)
1465 super(SOCurrencyCol, self).__init__(**kw)
1466
1467class CurrencyCol(DecimalCol):
1468 baseClass = SOCurrencyCol
1469
1470
1471class DecimalStringValidator(DecimalValidator):
1472 def to_python(self, value, state):
1473 value = super(DecimalStringValidator, self).to_python(value, state)
1474 if self.precision and isinstance(value, Decimal):
1475 assert value < self.max, "Value must be less than %s" % int(self.max)
1477 value = value.quantize(self.precision)
1478 return value
1479
1480 def from_python(self, value, state):
1481 value = super(DecimalStringValidator, self).from_python(value, state)
1482 if isinstance(value, Decimal):
1483 if self.precision:
1484 assert value < self.max, "Value must be less than %s" % int(self.max)
1486 value = value.quantize(self.precision)
1487 value = value.to_eng_string()
1488 elif isinstance(value, (int, long)):
1489 value = str(value)
1490 return value
1491
1492class SODecimalStringCol(SOStringCol):
1493 def __init__(self, **kw):
1494 self.size = kw.pop('size', NoDefault)
1495 assert (self.size is not NoDefault) and (self.size >= 0), "You must give a size argument as a positive integer"
1497 self.precision = kw.pop('precision', NoDefault)
1498 assert (self.precision is not NoDefault) and (self.precision >= 0), "You must give a precision argument as a positive integer"
1500 kw['length'] = int(self.size) + int(self.precision)
1501 self.quantize = kw.pop('quantize', False)
1502 assert isinstance(self.quantize, bool), "quantize argument must be Boolean True/False"
1504 super(SODecimalStringCol, self).__init__(**kw)
1505
1506 def createValidators(self):
1507 if self.quantize:
1508 v = DecimalStringValidator(name=self.name,
1509 precision=Decimal(10) ** (-1 * int(self.precision)),
1510 max=Decimal(10) ** (int(self.size) - int(self.precision)))
1511 else:
1512 v = DecimalStringValidator(name=self.name, precision=0)
1513 return [v] + super(SODecimalStringCol, self).createValidators(dataType=Decimal)
1515
1516class DecimalStringCol(StringCol):
1517 baseClass = SODecimalStringCol
1518
1519
1520class BinaryValidator(SOValidator):
1521 """
1522 Validator for binary types.
1523
1524 We're assuming that the per-database modules provide some form
1525 of wrapper type for binary conversion.
1526 """
1527
1528 _cachedValue = None
1529
1530 def to_python(self, value, state):
1531 if value is None:
1532 return None
1533 try:
1534 connection = state.connection or state.soObject._connection
1535 except AttributeError:
1536 dbName = None
1537 binaryType = type(None)
1538 else:
1539 dbName = connection.dbName
1540 binaryType = connection._binaryType
1541 if isinstance(value, str):
1542 if dbName == "sqlite":
1543 value = connection.module.decode(value)
1544 return value
1545 if isinstance(value, (buffer, binaryType)):
1546 cachedValue = self._cachedValue
1547 if cachedValue and cachedValue[1] == value:
1548 return cachedValue[0]
1549 if isinstance(value, array):
1550 return value.tostring()
1551 return str(value)
1552 raise validators.Invalid("expected a string in the BLOBCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1554
1555 def from_python(self, value, state):
1556 if value is None:
1557 return None
1558 connection = state.connection or state.soObject._connection
1559 binary = connection.createBinary(value)
1560 self._cachedValue = (value, binary)
1561 return binary
1562
1563class SOBLOBCol(SOStringCol):
1564 def __init__(self, **kw):
1565
1566 if 'varchar' not in kw: kw['varchar'] = False
1567 super(SOBLOBCol, self).__init__(**kw)
1568
1569 def createValidators(self):
1570 return [BinaryValidator(name=self.name)] + super(SOBLOBCol, self).createValidators()
1572
1573 def _mysqlType(self):
1574 length = self.length
1575 varchar = self.varchar
1576 if length >= 2**24:
1577 return varchar and "LONGTEXT" or "LONGBLOB"
1578 if length >= 2**16:
1579 return varchar and "MEDIUMTEXT" or "MEDIUMBLOB"
1580 if length >= 2**8:
1581 return varchar and "TEXT" or "BLOB"
1582 return varchar and "TINYTEXT" or "TINYBLOB"
1583
1584 def _postgresType(self):
1585 return 'BYTEA'
1586
1587 def _mssqlType(self):
1588 if self.connection and self.connection.can_use_max_types():
1589 return 'VARBINARY(MAX)'
1590 else:
1591 return "IMAGE"
1592
1593class BLOBCol(StringCol):
1594 baseClass = SOBLOBCol
1595
1596
1597class PickleValidator(BinaryValidator):
1598 """
1599 Validator for pickle types. A pickle type is simply a binary type
1600 with hidden pickling, so that we can simply store any kind of
1601 stuff in a particular column.
1602
1603 The support for this relies directly on the support for binary for
1604 your database.
1605 """
1606
1607 def to_python(self, value, state):
1608 if value is None:
1609 return None
1610 if isinstance(value, unicode):
1611 dbEncoding = self.getDbEncoding(state, default='ascii')
1612 value = value.encode(dbEncoding)
1613 if isinstance(value, str):
1614 return pickle.loads(value)
1615 raise validators.Invalid("expected a pickle string in the PickleCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1617
1618 def from_python(self, value, state):
1619 if value is None:
1620 return None
1621 return pickle.dumps(value, self.pickleProtocol)
1622
1623class SOPickleCol(SOBLOBCol):
1624
1625 def __init__(self, **kw):
1626 self.pickleProtocol = kw.pop('pickleProtocol', pickle.HIGHEST_PROTOCOL)
1627 super(SOPickleCol, self).__init__(**kw)
1628
1629 def createValidators(self):
1630 return [PickleValidator(name=self.name,
1631 pickleProtocol=self.pickleProtocol)] + super(SOPickleCol, self).createValidators()
1633
1634 def _mysqlType(self):
1635 length = self.length
1636 if length >= 2**24:
1637 return "LONGBLOB"
1638 if length >= 2**16:
1639 return "MEDIUMBLOB"
1640 return "BLOB"
1641
1642class PickleCol(BLOBCol):
1643 baseClass = SOPickleCol
1644
1645
1646def pushKey(kw, name, value):
1647 if not name in kw:
1648 kw[name] = value
1649
1650all = []
1651for key, value in globals().items():
1652 if isinstance(value, type) and (issubclass(value, (Col, SOCol))):
1653 all.append(key)
1654__all__.extend(all)
1655del all