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):
0324 return ' '.join([self.dbName, self._mysqlType()] + self._extraSQL())
0325
0326 def postgresCreateSQL(self):
0327 return ' '.join([self.dbName, self._postgresType()] + self._extraSQL())
0328
0329 def sqliteCreateSQL(self):
0330 return ' '.join([self.dbName, self._sqliteType()] + self._extraSQL())
0331
0332 def sybaseCreateSQL(self):
0333 return ' '.join([self.dbName, self._sybaseType()] + self._extraSQL())
0334
0335 def mssqlCreateSQL(self, connection=None):
0336 self.connection = connection
0337 return ' '.join([self.dbName, self._mssqlType()] + self._extraSQL())
0338
0339 def firebirdCreateSQL(self):
0340
0341
0342
0343 if not isinstance(self, SOEnumCol):
0344 return ' '.join([self.dbName, self._firebirdType()] + self._extraSQL())
0345 else:
0346 return ' '.join([self.dbName] + [self._firebirdType()[0]] + self._extraSQL() + [self._firebirdType()[1]])
0347
0348 def maxdbCreateSQL(self):
0349 return ' '.join([self.dbName, self._maxdbType()] + self._extraSQL())
0350
0351 def __get__(self, obj, type=None):
0352 if obj is None:
0353
0354 return self
0355 if obj.sqlmeta._obsolete:
0356 raise RuntimeError('The object <%s %s> is obsolete' % (
0357 obj.__class__.__name__, obj.id))
0358 if obj.sqlmeta.cacheColumns:
0359 columns = obj.sqlmeta._columnCache
0360 if columns is None:
0361 obj.sqlmeta.loadValues()
0362 try:
0363 return columns[name]
0364 except KeyError:
0365 return obj.sqlmeta.loadColumn(self)
0366 else:
0367 return obj.sqlmeta.loadColumn(self)
0368
0369 def __set__(self, obj, value):
0370 if self.immutable:
0371 raise AttributeError("The column %s.%s is immutable" %
0372 (obj.__class__.__name__,
0373 self.name))
0374 obj.sqlmeta.setColumn(self, value)
0375
0376 def __delete__(self, obj):
0377 raise AttributeError("I can't be deleted from %r" % obj)
0378
0379 def getDbEncoding(self, state, default='utf-8'):
0380 if self.dbEncoding:
0381 return self.dbEncoding
0382 dbEncoding = state.soObject.sqlmeta.dbEncoding
0383 if dbEncoding:
0384 return dbEncoding
0385 try:
0386 connection = state.connection or state.soObject._connection
0387 except AttributeError:
0388 dbEncoding = None
0389 else:
0390 dbEncoding = getattr(connection, "dbEncoding", None)
0391 if not dbEncoding:
0392 dbEncoding = default
0393 return dbEncoding
0394
0395
0396class Col(object):
0397
0398 baseClass = SOCol
0399
0400 def __init__(self, name=None, **kw):
0401 super(Col, self).__init__()
0402 self.__dict__['_name'] = name
0403 self.__dict__['_kw'] = kw
0404 self.__dict__['creationOrder'] = creationOrder.next()
0405 self.__dict__['_extra_vars'] = {}
0406
0407 def _set_name(self, value):
0408 assert self._name is None or self._name == value, (
0409 "You cannot change a name after it has already been set "
0410 "(from %s to %s)" % (self.name, value))
0411 self.__dict__['_name'] = value
0412
0413 def _get_name(self):
0414 return self._name
0415
0416 name = property(_get_name, _set_name)
0417
0418 def withClass(self, soClass):
0419 return self.baseClass(soClass=soClass, name=self._name,
0420 creationOrder=self.creationOrder,
0421 columnDef=self,
0422 extra_vars=self._extra_vars,
0423 **self._kw)
0424
0425 def __setattr__(self, var, value):
0426 if var == 'name':
0427 super(Col, self).__setattr__(var, value)
0428 return
0429 self._extra_vars[var] = value
0430
0431 def __repr__(self):
0432 return '<%s %s %s>' % (
0433 self.__class__.__name__, hex(abs(id(self)))[2:],
0434 self._name or '(unnamed)')
0435
0436
0437class SOValidator(validators.Validator):
0438 def getDbEncoding(self, state, default='utf-8'):
0439 try:
0440 return self.dbEncoding
0441 except AttributeError:
0442 return self.soCol.getDbEncoding(state, default=default)
0443
0444
0445class SOStringLikeCol(SOCol):
0446 """A common ancestor for SOStringCol and SOUnicodeCol"""
0447 def __init__(self, **kw):
0448 self.length = kw.pop('length', None)
0449 self.varchar = kw.pop('varchar', 'auto')
0450 self.char_binary = kw.pop('char_binary', None)
0451 if not self.length:
0452 assert self.varchar == 'auto' or not self.varchar, "Without a length strings are treated as TEXT, not varchar"
0454 self.varchar = False
0455 elif self.varchar == 'auto':
0456 self.varchar = True
0457
0458 super(SOStringLikeCol, self).__init__(**kw)
0459
0460 def autoConstraints(self):
0461 constraints = [constrs.isString]
0462 if self.length is not None:
0463 constraints += [constrs.MaxLength(self.length)]
0464 return constraints
0465
0466 def _sqlType(self):
0467 if self.customSQLType is not None:
0468 return self.customSQLType
0469 if not self.length:
0470 return 'TEXT'
0471 elif self.varchar:
0472 return 'VARCHAR(%i)' % self.length
0473 else:
0474 return 'CHAR(%i)' % self.length
0475
0476 def _check_case_sensitive(self, db):
0477 if self.char_binary:
0478 raise ValueError, "%s does not support binary character columns" % db
0479
0480 def _mysqlType(self):
0481 type = self._sqlType()
0482 if self.char_binary:
0483 type += " BINARY"
0484 return type
0485
0486 def _postgresType(self):
0487 self._check_case_sensitive("PostgreSQL")
0488 return super(SOStringLikeCol, self)._postgresType()
0489
0490 def _sqliteType(self):
0491 self._check_case_sensitive("SQLite")
0492 return super(SOStringLikeCol, self)._sqliteType()
0493
0494 def _sybaseType(self):
0495 self._check_case_sensitive("SYBASE")
0496 type = self._sqlType()
0497 if not self.notNone and not self.alternateID:
0498 type += ' NULL'
0499 return type
0500
0501 def _mssqlType(self):
0502 if self.customSQLType is not None:
0503 return self.customSQLType
0504 if not self.length:
0505 if self.connection and self.connection.can_use_max_types():
0506 type = 'VARCHAR(MAX)'
0507 else:
0508 type = 'VARCHAR(4000)'
0509 elif self.varchar:
0510 type = 'VARCHAR(%i)' % self.length
0511 else:
0512 type = 'CHAR(%i)' % self.length
0513 if not self.notNone and not self.alternateID:
0514 type += ' NULL'
0515 return type
0516
0517 def _firebirdType(self):
0518 self._check_case_sensitive("FireBird")
0519 if not self.length:
0520 return 'BLOB SUB_TYPE TEXT'
0521 else:
0522 return self._sqlType()
0523
0524 def _maxdbType(self):
0525 self._check_case_sensitive("SAP DB/MaxDB")
0526 if not self.length:
0527 return 'LONG ASCII'
0528 else:
0529 return self._sqlType()
0530
0531
0532class StringValidator(SOValidator):
0533
0534 def to_python(self, value, state):
0535 if value is None:
0536 return None
0537 try:
0538 connection = state.connection or state.soObject._connection
0539 binaryType = connection._binaryType
0540 except AttributeError:
0541 binaryType = type(None)
0542 dbEncoding = self.getDbEncoding(state, default='ascii')
0543 if isinstance(value, unicode):
0544 return value.encode(dbEncoding)
0545 if self.dataType and isinstance(value, self.dataType):
0546 return value
0547 if isinstance(value, (str, buffer, binaryType, sqlbuilder.SQLExpression)):
0548 return value
0549 if hasattr(value, '__unicode__'):
0550 return unicode(value).encode(dbEncoding)
0551 raise validators.Invalid("expected a str in the StringCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
0553
0554 from_python = to_python
0555
0556class SOStringCol(SOStringLikeCol):
0557
0558 def createValidators(self, dataType=None):
0559 return [StringValidator(name=self.name, dataType=dataType)] + super(SOStringCol, self).createValidators()
0561
0562class StringCol(Col):
0563 baseClass = SOStringCol
0564
0565
0566class NQuoted(sqlbuilder.SQLExpression):
0567 def __init__(self, value):
0568 assert isinstance(value, unicode)
0569 self.value = value
0570 def __hash__(self):
0571 return hash(self.value)
0572 def __sqlrepr__(self, db):
0573 assert db == 'mssql'
0574 return "N" + sqlbuilder.sqlrepr(self.value, db)
0575
0576class UnicodeStringValidator(SOValidator):
0577
0578 def to_python(self, value, state):
0579 if value is None:
0580 return None
0581 if isinstance(value, (unicode, sqlbuilder.SQLExpression)):
0582 return value
0583 if isinstance(value, str):
0584 return unicode(value, self.getDbEncoding(state))
0585 if isinstance(value, array):
0586 return unicode(value.tostring(), self.getDbEncoding(state))
0587 if hasattr(value, '__unicode__'):
0588 return unicode(value)
0589 raise validators.Invalid("expected a str or a unicode in the UnicodeCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
0591
0592 def from_python(self, value, state):
0593 if value is None:
0594 return None
0595 if isinstance(value, (str, sqlbuilder.SQLExpression)):
0596 return value
0597 if isinstance(value, unicode):
0598 try:
0599 connection = state.connection or state.soObject._connection
0600 except AttributeError:
0601 pass
0602 else:
0603 if connection.dbName == 'mssql':
0604 return NQuoted(value)
0605 return value.encode(self.getDbEncoding(state))
0606 if hasattr(value, '__unicode__'):
0607 return unicode(value).encode(self.getDbEncoding(state))
0608 raise validators.Invalid("expected a str or a unicode in the UnicodeCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
0610
0611class SOUnicodeCol(SOStringLikeCol):
0612 def _mssqlType(self):
0613 if self.customSQLType is not None:
0614 return self.customSQLType
0615 return 'N' + super(SOUnicodeCol, self)._mssqlType()
0616
0617 def createValidators(self):
0618 return [UnicodeStringValidator(name=self.name)] + super(SOUnicodeCol, self).createValidators()
0620
0621class UnicodeCol(Col):
0622 baseClass = SOUnicodeCol
0623
0624
0625class IntValidator(SOValidator):
0626
0627 def to_python(self, value, state):
0628 if value is None:
0629 return None
0630 if isinstance(value, (int, long, sqlbuilder.SQLExpression)):
0631 return value
0632 for converter, attr_name in (int, '__int__'), (long, '__long__'):
0633 if hasattr(value, attr_name):
0634 try:
0635 return converter(value)
0636 except:
0637 break
0638 raise validators.Invalid("expected an int in the IntCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
0640
0641 from_python = to_python
0642
0643class SOIntCol(SOCol):
0644
0645 def __init__(self, **kw):
0646 self.length = kw.pop('length', None)
0647 self.unsigned = bool(kw.pop('unsigned', None))
0648 self.zerofill = bool(kw.pop('zerofill', None))
0649 SOCol.__init__(self, **kw)
0650
0651 def autoConstraints(self):
0652 return [constrs.isInt]
0653
0654 def createValidators(self):
0655 return [IntValidator(name=self.name)] + super(SOIntCol, self).createValidators()
0657
0658 def addSQLAttrs(self, str):
0659 _ret = str
0660 if str is None or len(str) < 1:
0661 return None
0662
0663 if self.length >= 1:
0664 _ret = "%s(%d)" % (_ret, self.length)
0665 if self.unsigned:
0666 _ret = _ret + " UNSIGNED"
0667 if self.zerofill:
0668 _ret = _ret + " ZEROFILL"
0669 return _ret
0670
0671 def _sqlType(self):
0672 return self.addSQLAttrs("INT")
0673
0674class IntCol(Col):
0675 baseClass = SOIntCol
0676
0677class SOTinyIntCol(SOIntCol):
0678 def _sqlType(self):
0679 return self.addSQLAttrs("TINYINT")
0680
0681class TinyIntCol(Col):
0682 baseClass = SOTinyIntCol
0683
0684class SOSmallIntCol(SOIntCol):
0685 def _sqlType(self):
0686 return self.addSQLAttrs("SMALLINT")
0687
0688class SmallIntCol(Col):
0689 baseClass = SOSmallIntCol
0690
0691class SOMediumIntCol(SOIntCol):
0692 def _sqlType(self):
0693 return self.addSQLAttrs("MEDIUMINT")
0694
0695class MediumIntCol(Col):
0696 baseClass = SOMediumIntCol
0697
0698class SOBigIntCol(SOIntCol):
0699 def _sqlType(self):
0700 return self.addSQLAttrs("BIGINT")
0701
0702class BigIntCol(Col):
0703 baseClass = SOBigIntCol
0704
0705
0706class BoolValidator(SOValidator):
0707
0708 def to_python(self, value, state):
0709 if value is None:
0710 return None
0711 if isinstance(value, (bool, sqlbuilder.SQLExpression)):
0712 return value
0713 if isinstance(value, (int, long)) or hasattr(value, '__nonzero__'):
0714 return bool(value)
0715 raise validators.Invalid("expected a bool or an int in the BoolCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
0717
0718 from_python = to_python
0719
0720class SOBoolCol(SOCol):
0721 def autoConstraints(self):
0722 return [constrs.isBool]
0723
0724 def createValidators(self):
0725 return [BoolValidator(name=self.name)] + super(SOBoolCol, self).createValidators()
0727
0728 def _postgresType(self):
0729 return 'BOOL'
0730
0731 def _mysqlType(self):
0732 return "BOOL"
0733
0734 def _sybaseType(self):
0735 return "BIT"
0736
0737 def _mssqlType(self):
0738 return "BIT"
0739
0740 def _firebirdType(self):
0741 return 'INT'
0742
0743 def _maxdbType(self):
0744 return "BOOLEAN"
0745
0746 def _sqliteType(self):
0747 return "BOOLEAN"
0748
0749class BoolCol(Col):
0750 baseClass = SOBoolCol
0751
0752
0753class FloatValidator(SOValidator):
0754
0755 def to_python(self, value, state):
0756 if value is None:
0757 return None
0758 if isinstance(value, (float, int, long, sqlbuilder.SQLExpression)):
0759 return value
0760 for converter, attr_name in (float, '__float__'), (int, '__int__'), (long, '__long__'):
0761 if hasattr(value, attr_name):
0762 try:
0763 return converter(value)
0764 except:
0765 break
0766 raise validators.Invalid("expected a float in the FloatCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
0768
0769 from_python = to_python
0770
0771class SOFloatCol(SOCol):
0772
0773
0774 def autoConstraints(self):
0775 return [constrs.isFloat]
0776
0777 def createValidators(self):
0778 return [FloatValidator(name=self.name)] + super(SOFloatCol, self).createValidators()
0780
0781 def _sqlType(self):
0782 return 'FLOAT'
0783
0784 def _mysqlType(self):
0785 return "DOUBLE PRECISION"
0786
0787class FloatCol(Col):
0788 baseClass = SOFloatCol
0789
0790
0791class SOKeyCol(SOCol):
0792 key_type = {int: "INT", str: "TEXT"}
0793
0794
0795
0796
0797 def __init__(self, **kw):
0798 self.refColumn = kw.pop('refColumn', None)
0799 super(SOKeyCol, self).__init__(**kw)
0800
0801 def _sqlType(self):
0802 return self.key_type[self.soClass.sqlmeta.idType]
0803
0804 def _sybaseType(self):
0805 key_type = {int: "NUMERIC(18,0) NULL", str: "TEXT"}
0806 return key_type[self.soClass.sqlmeta.idType]
0807
0808 def _mssqlType(self):
0809 key_type = {int: "INT NULL", str: "TEXT"}
0810 return key_type[self.soClass.sqlmeta.idType]
0811
0812class KeyCol(Col):
0813
0814 baseClass = SOKeyCol
0815
0816class SOForeignKey(SOKeyCol):
0817
0818 def __init__(self, **kw):
0819 foreignKey = kw['foreignKey']
0820 style = kw['soClass'].sqlmeta.style
0821 if kw.get('name'):
0822 kw['origName'] = kw['name']
0823 kw['name'] = style.instanceAttrToIDAttr(kw['name'])
0824 else:
0825 kw['name'] = style.instanceAttrToIDAttr(style.pythonClassToAttr(foreignKey))
0826 super(SOForeignKey, self).__init__(**kw)
0827
0828 def sqliteCreateSQL(self):
0829 sql = SOKeyCol.sqliteCreateSQL(self)
0830 other = findClass(self.foreignKey, self.soClass.sqlmeta.registry)
0831 tName = other.sqlmeta.table
0832 idName = self.refColumn or other.sqlmeta.idName
0833 if self.cascade is not None:
0834 if self.cascade == 'null':
0835 action = 'ON DELETE SET NULL'
0836 elif self.cascade:
0837 action = 'ON DELETE CASCADE'
0838 else:
0839 action = 'ON DELETE RESTRICT'
0840 else:
0841 action = ''
0842 constraint = ('CONSTRAINT %(colName)s_exists '
0843
0844 'REFERENCES %(tName)s(%(idName)s) '
0845 '%(action)s' %
0846 {'tName': tName,
0847 'colName': self.dbName,
0848 'idName': idName,
0849 'action': action})
0850 sql = ' '.join([sql, constraint])
0851 return sql
0852
0853 def postgresCreateSQL(self):
0854 sql = SOKeyCol.postgresCreateSQL(self)
0855 return sql
0856
0857 def postgresCreateReferenceConstraint(self):
0858 sTName = self.soClass.sqlmeta.table
0859 other = findClass(self.foreignKey, self.soClass.sqlmeta.registry)
0860 tName = other.sqlmeta.table
0861 idName = self.refColumn or other.sqlmeta.idName
0862 if self.cascade is not None:
0863 if self.cascade == 'null':
0864 action = 'ON DELETE SET NULL'
0865 elif self.cascade:
0866 action = 'ON DELETE CASCADE'
0867 else:
0868 action = 'ON DELETE RESTRICT'
0869 else:
0870 action = ''
0871 constraint = ('ALTER TABLE %(sTName)s ADD CONSTRAINT %(colName)s_exists '
0872 'FOREIGN KEY (%(colName)s) '
0873 'REFERENCES %(tName)s (%(idName)s) '
0874 '%(action)s' %
0875 {'tName': tName,
0876 'colName': self.dbName,
0877 'idName': idName,
0878 'action': action,
0879 'sTName': sTName})
0880 return constraint
0881
0882 def mysqlCreateReferenceConstraint(self):
0883 sTName = self.soClass.sqlmeta.table
0884 sTLocalName = sTName.split('.')[-1]
0885 other = findClass(self.foreignKey, self.soClass.sqlmeta.registry)
0886 tName = other.sqlmeta.table
0887 idName = self.refColumn or other.sqlmeta.idName
0888 if self.cascade is not None:
0889 if self.cascade == 'null':
0890 action = 'ON DELETE SET NULL'
0891 elif self.cascade:
0892 action = 'ON DELETE CASCADE'
0893 else:
0894 action = 'ON DELETE RESTRICT'
0895 else:
0896 action = ''
0897 constraint = ('ALTER TABLE %(sTName)s ADD CONSTRAINT %(sTLocalName)s_%(colName)s_exists '
0898 'FOREIGN KEY (%(colName)s) '
0899 'REFERENCES %(tName)s (%(idName)s) '
0900 '%(action)s' %
0901 {'tName': tName,
0902 'colName': self.dbName,
0903 'idName': idName,
0904 'action': action,
0905 'sTName': sTName,
0906 'sTLocalName': sTLocalName})
0907 return constraint
0908
0909 def mysqlCreateSQL(self):
0910 return SOKeyCol.mysqlCreateSQL(self)
0911
0912 def sybaseCreateSQL(self):
0913 sql = SOKeyCol.sybaseCreateSQL(self)
0914 other = findClass(self.foreignKey, self.soClass.sqlmeta.registry)
0915 tName = other.sqlmeta.table
0916 idName = self.refColumn or other.sqlmeta.idName
0917 reference = ('REFERENCES %(tName)s(%(idName)s) ' %
0918 {'tName':tName,
0919 'idName':idName})
0920 sql = ' '.join([sql, reference])
0921 return sql
0922
0923 def sybaseCreateReferenceConstraint(self):
0924
0925 return None
0926
0927 def mssqlCreateSQL(self, connection=None):
0928 sql = SOKeyCol.mssqlCreateSQL(self, connection)
0929 other = findClass(self.foreignKey, self.soClass.sqlmeta.registry)
0930 tName = other.sqlmeta.table
0931 idName = self.refColumn or other.sqlmeta.idName
0932 reference = ('REFERENCES %(tName)s(%(idName)s) ' %
0933 {'tName':tName,
0934 'idName':idName})
0935 sql = ' '.join([sql, reference])
0936 return sql
0937
0938 def mssqlCreateReferenceConstraint(self):
0939
0940 return None
0941
0942 def maxdbCreateSQL(self):
0943 other = findClass(self.foreignKey, self.soClass.sqlmeta.registry)
0944 fidName = self.dbName
0945
0946 sql = ' '.join([fidName, self._maxdbType()])
0947 tName = other.sqlmeta.table
0948 idName = self.refColumn or other.sqlmeta.idName
0949 sql=sql + ',' + '\n'
0950 sql=sql + 'FOREIGN KEY (%s) REFERENCES %s(%s)'%(fidName,tName,idName)
0951 return sql
0952
0953 def maxdbCreateReferenceConstraint(self):
0954
0955 return None
0956
0957class ForeignKey(KeyCol):
0958
0959 baseClass = SOForeignKey
0960
0961 def __init__(self, foreignKey=None, **kw):
0962 super(ForeignKey, self).__init__(foreignKey=foreignKey, **kw)
0963
0964
0965class EnumValidator(SOValidator):
0966
0967 def to_python(self, value, state):
0968 if value in self.enumValues:
0969 if isinstance(value, unicode):
0970 dbEncoding = self.getDbEncoding(state)
0971 value = value.encode(dbEncoding)
0972 return value
0973 elif not self.notNone and value is None:
0974 return None
0975 raise validators.Invalid("expected a member of %r in the EnumCol '%s', got %r instead" % (self.enumValues, self.name, value), value, state)
0977
0978 from_python = to_python
0979
0980class SOEnumCol(SOCol):
0981
0982 def __init__(self, **kw):
0983 self.enumValues = kw.pop('enumValues', None)
0984 assert self.enumValues is not None, 'You must provide an enumValues keyword argument'
0986 super(SOEnumCol, self).__init__(**kw)
0987
0988 def autoConstraints(self):
0989 return [constrs.isString, constrs.InList(self.enumValues)]
0990
0991 def createValidators(self):
0992 return [EnumValidator(name=self.name, enumValues=self.enumValues,
0993 notNone=self.notNone)] + super(SOEnumCol, self).createValidators()
0995
0996 def _mysqlType(self):
0997
0998
0999 if None in self.enumValues:
1000 return "ENUM(%s)" % ', '.join([sqlbuilder.sqlrepr(v, 'mysql') for v in self.enumValues if v is not None])
1001 else:
1002 return "ENUM(%s) NOT NULL" % ', '.join([sqlbuilder.sqlrepr(v, 'mysql') for v in self.enumValues])
1003
1004 def _postgresType(self):
1005 length = max(map(self._getlength, self.enumValues))
1006 enumValues = ', '.join([sqlbuilder.sqlrepr(v, 'postgres') for v in self.enumValues])
1007 checkConstraint = "CHECK (%s in (%s))" % (self.dbName, enumValues)
1008 return "VARCHAR(%i) %s" % (length, checkConstraint)
1009
1010 _sqliteType = _postgresType
1011
1012 def _sybaseType(self):
1013 return self._postgresType()
1014
1015 def _mssqlType(self):
1016 return self._postgresType()
1017
1018 def _firebirdType(self):
1019 length = max(map(self._getlength, self.enumValues))
1020 enumValues = ', '.join([sqlbuilder.sqlrepr(v, 'firebird') for v in self.enumValues])
1021 checkConstraint = "CHECK (%s in (%s))" % (self.dbName, enumValues)
1022
1023 return "VARCHAR(%i)" % (length), checkConstraint
1024
1025 def _maxdbType(self):
1026 raise TypeError("Enum type is not supported on MAX DB")
1027
1028 def _getlength(self, obj):
1029 """
1030 None counts as 0; everything else uses len()
1031 """
1032 if obj is None:
1033 return 0
1034 else:
1035 return len(obj)
1036
1037class EnumCol(Col):
1038 baseClass = SOEnumCol
1039
1040
1041class SetValidator(SOValidator):
1042 """
1043 Translates Python tuples into SQL comma-delimited SET strings.
1044 """
1045
1046 def to_python(self, value, state):
1047 if isinstance(value, str):
1048 return tuple(value.split(","))
1049 raise validators.Invalid("expected a string in the SetCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1051
1052 def from_python(self, value, state):
1053 if isinstance(value, basestring):
1054 value = (value,)
1055 try:
1056 return ",".join(value)
1057 except:
1058 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)
1060
1061class SOSetCol(SOCol):
1062 def __init__(self, **kw):
1063 self.setValues = kw.pop('setValues', None)
1064 assert self.setValues is not None, 'You must provide a setValues keyword argument'
1066 super(SOSetCol, self).__init__(**kw)
1067
1068 def autoConstraints(self):
1069 return [constrs.isString, constrs.InList(self.setValues)]
1070
1071 def createValidators(self):
1072 return [SetValidator(name=self.name, setValues=self.setValues)] + super(SOSetCol, self).createValidators()
1074
1075 def _mysqlType(self):
1076 return "SET(%s)" % ', '.join([sqlbuilder.sqlrepr(v, 'mysql') for v in self.setValues])
1077
1078class SetCol(Col):
1079 baseClass = SOSetCol
1080
1081
1082class DateTimeValidator(validators.DateValidator):
1083 def to_python(self, value, state):
1084 if value is None:
1085 return None
1086 if isinstance(value, (datetime.datetime, datetime.date, datetime.time, sqlbuilder.SQLExpression)):
1087 return value
1088 if mxdatetime_available:
1089 if isinstance(value, DateTimeType):
1090
1091 if (self.format.find("%H") >= 0) or (self.format.find("%T")) >= 0:
1092 return datetime.datetime(value.year, value.month, value.day,
1093 value.hour, value.minute, int(value.second))
1094 else:
1095 return datetime.date(value.year, value.month, value.day)
1096 elif isinstance(value, TimeType):
1097
1098 if self.format.find("%d") >= 0:
1099 return datetime.timedelta(seconds=value.seconds)
1100 else:
1101 return datetime.time(value.hour, value.minute, int(value.second))
1102 try:
1103 if self.format.find(".%f") >= 0:
1104 if '.' in value:
1105 _value = value.split('.')
1106 microseconds = _value[-1]
1107 _l = len(microseconds)
1108 if _l < 6:
1109 _value[-1] = microseconds + '0'*(6 - _l)
1110 elif _l > 6:
1111 _value[-1] = microseconds[:6]
1112 if _l != 6:
1113 value = '.'.join(_value)
1114 else:
1115 value += '.0'
1116 return datetime.datetime.strptime(value, self.format)
1117 except:
1118 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)
1120
1121 def from_python(self, value, state):
1122 if value is None:
1123 return None
1124 if isinstance(value, (datetime.datetime, datetime.date, datetime.time, sqlbuilder.SQLExpression)):
1125 return value
1126 if hasattr(value, "strftime"):
1127 return value.strftime(self.format)
1128 raise validators.Invalid("expected a datetime in the DateTimeCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1130
1131if mxdatetime_available:
1132 class MXDateTimeValidator(validators.DateValidator):
1133 def to_python(self, value, state):
1134 if value is None:
1135 return None
1136 if isinstance(value, (DateTimeType, TimeType, sqlbuilder.SQLExpression)):
1137 return value
1138 if isinstance(value, datetime.datetime):
1139 return DateTime.DateTime(value.year, value.month, value.day,
1140 value.hour, value.minute, value.second)
1141 elif isinstance(value, datetime.date):
1142 return DateTime.Date(value.year, value.month, value.day)
1143 elif isinstance(value, datetime.time):
1144 return DateTime.Time(value.hour, value.minute, value.second)
1145 try:
1146 if self.format.find(".%f") >= 0:
1147 if '.' in value:
1148 _value = value.split('.')
1149 microseconds = _value[-1]
1150 _l = len(microseconds)
1151 if _l < 6:
1152 _value[-1] = microseconds + '0'*(6 - _l)
1153 elif _l > 6:
1154 _value[-1] = microseconds[:6]
1155 if _l != 6:
1156 value = '.'.join(_value)
1157 else:
1158 value += '.0'
1159 value = datetime.datetime.strptime(value, self.format)
1160 return DateTime.DateTime(value.year, value.month, value.day,
1161 value.hour, value.minute, value.second)
1162 except:
1163 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)
1165
1166 def from_python(self, value, state):
1167 if value is None:
1168 return None
1169 if isinstance(value, (DateTimeType, TimeType, sqlbuilder.SQLExpression)):
1170 return value
1171 if hasattr(value, "strftime"):
1172 return value.strftime(self.format)
1173 raise validators.Invalid("expected a mxDateTime in the DateTimeCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1175
1176class SODateTimeCol(SOCol):
1177 datetimeFormat = '%Y-%m-%d %H:%M:%S.%f'
1178
1179 def __init__(self, **kw):
1180 datetimeFormat = kw.pop('datetimeFormat', None)
1181 if datetimeFormat:
1182 self.datetimeFormat = datetimeFormat
1183 super(SODateTimeCol, self).__init__(**kw)
1184
1185 def createValidators(self):
1186 _validators = super(SODateTimeCol, self).createValidators()
1187 if default_datetime_implementation == DATETIME_IMPLEMENTATION:
1188 validatorClass = DateTimeValidator
1189 elif default_datetime_implementation == MXDATETIME_IMPLEMENTATION:
1190 validatorClass = MXDateTimeValidator
1191 if default_datetime_implementation:
1192 _validators.insert(0, validatorClass(name=self.name, format=self.datetimeFormat))
1193 return _validators
1194
1195 def _mysqlType(self):
1196 return 'DATETIME'
1197
1198 def _postgresType(self):
1199 return 'TIMESTAMP'
1200
1201 def _sybaseType(self):
1202 return 'DATETIME'
1203
1204 def _mssqlType(self):
1205 return 'DATETIME'
1206
1207 def _sqliteType(self):
1208 return 'TIMESTAMP'
1209
1210 def _firebirdType(self):
1211 return 'TIMESTAMP'
1212
1213 def _maxdbType(self):
1214 return 'TIMESTAMP'
1215
1216class DateTimeCol(Col):
1217 baseClass = SODateTimeCol
1218 @staticmethod
1219 def now():
1220 if default_datetime_implementation == DATETIME_IMPLEMENTATION:
1221 return datetime.datetime.now()
1222 elif default_datetime_implementation == MXDATETIME_IMPLEMENTATION:
1223 return DateTime.now()
1224 else:
1225 assert 0, ("No datetime implementation available "
1226 "(DATETIME_IMPLEMENTATION=%r)"
1227 % DATETIME_IMPLEMENTATION)
1228
1229
1230class DateValidator(DateTimeValidator):
1231 def to_python(self, value, state):
1232 if isinstance(value, datetime.datetime):
1233 value = value.date()
1234 if isinstance(value, (datetime.date, sqlbuilder.SQLExpression)):
1235 return value
1236 value = super(DateValidator, self).to_python(value, state)
1237 if isinstance(value, datetime.datetime):
1238 value = value.date()
1239 return value
1240
1241 from_python = to_python
1242
1243class SODateCol(SOCol):
1244 dateFormat = '%Y-%m-%d'
1245
1246 def __init__(self, **kw):
1247 dateFormat = kw.pop('dateFormat', None)
1248 if dateFormat: self.dateFormat = dateFormat
1249 super(SODateCol, self).__init__(**kw)
1250
1251 def createValidators(self):
1252 """Create a validator for the column. Can be overriden in descendants."""
1253 _validators = super(SODateCol, self).createValidators()
1254 if default_datetime_implementation == DATETIME_IMPLEMENTATION:
1255 validatorClass = DateValidator
1256 elif default_datetime_implementation == MXDATETIME_IMPLEMENTATION:
1257 validatorClass = MXDateTimeValidator
1258 if default_datetime_implementation:
1259 _validators.insert(0, validatorClass(name=self.name, format=self.dateFormat))
1260 return _validators
1261
1262 def _mysqlType(self):
1263 return 'DATE'
1264
1265 def _postgresType(self):
1266 return 'DATE'
1267
1268 def _sybaseType(self):
1269 return self._postgresType()
1270
1271 def _mssqlType(self):
1272 """
1273 SQL Server doesn't have a DATE data type, to emulate we use a vc(10)
1274 """
1275 return 'VARCHAR(10)'
1276
1277 def _firebirdType(self):
1278 return 'DATE'
1279
1280 def _maxdbType(self):
1281 return 'DATE'
1282
1283 def _sqliteType(self):
1284 return 'DATE'
1285
1286class DateCol(Col):
1287 baseClass = SODateCol
1288
1289
1290class TimeValidator(DateTimeValidator):
1291 def to_python(self, value, state):
1292 if isinstance(value, (datetime.time, sqlbuilder.SQLExpression)):
1293 return value
1294 if isinstance(value, datetime.timedelta):
1295 if value.days:
1296 raise validators.Invalid(
1297 "the value for the TimeCol '%s' must has days=0, it has days=%d" %
1298 (self.name, value.days), value, state)
1299 return datetime.time(*time.gmtime(value.seconds)[3:6])
1300 value = super(TimeValidator, self).to_python(value, state)
1301 if isinstance(value, datetime.datetime):
1302 value = value.time()
1303 return value
1304
1305 from_python = to_python
1306
1307class SOTimeCol(SOCol):
1308 timeFormat = '%H:%M:%S.%f'
1309
1310 def __init__(self, **kw):
1311 timeFormat = kw.pop('timeFormat', None)
1312 if timeFormat:
1313 self.timeFormat = timeFormat
1314 super(SOTimeCol, self).__init__(**kw)
1315
1316 def createValidators(self):
1317 _validators = super(SOTimeCol, self).createValidators()
1318 if default_datetime_implementation == DATETIME_IMPLEMENTATION:
1319 validatorClass = TimeValidator
1320 elif default_datetime_implementation == MXDATETIME_IMPLEMENTATION:
1321 validatorClass = MXDateTimeValidator
1322 if default_datetime_implementation:
1323 _validators.insert(0, validatorClass(name=self.name, format=self.timeFormat))
1324 return _validators
1325
1326 def _mysqlType(self):
1327 return 'TIME'
1328
1329 def _postgresType(self):
1330 return 'TIME'
1331
1332 def _sybaseType(self):
1333 return 'TIME'
1334
1335 def _sqliteType(self):
1336 return 'TIME'
1337
1338 def _firebirdType(self):
1339 return 'TIME'
1340
1341 def _maxdbType(self):
1342 return 'TIME'
1343
1344class TimeCol(Col):
1345 baseClass = SOTimeCol
1346
1347
1348class SOTimestampCol(SODateTimeCol):
1349 """
1350 Necessary to support MySQL's use of TIMESTAMP versus DATETIME types
1351 """
1352
1353 def __init__(self, **kw):
1354 if 'default' not in kw:
1355 kw['default'] = None
1356 SOCol.__init__(self, **kw)
1357
1358 def _mysqlType(self):
1359 return 'TIMESTAMP'
1360
1361class TimestampCol(Col):
1362 baseClass = SOTimestampCol
1363
1364
1365class TimedeltaValidator(SOValidator):
1366 def to_python(self, value, state):
1367 return value
1368
1369 from_python = to_python
1370
1371class SOTimedeltaCol(SOCol):
1372 def _postgresType(self):
1373 return 'INTERVAL'
1374
1375 def createValidators(self):
1376 return [TimedeltaValidator(name=self.name)] + super(SOTimedeltaCol, self).createValidators()
1378
1379class TimedeltaCol(Col):
1380 baseClass = SOTimedeltaCol
1381
1382
1383from decimal import Decimal
1384
1385class DecimalValidator(SOValidator):
1386 def to_python(self, value, state):
1387 if value is None:
1388 return None
1389 if isinstance(value, (int, long, Decimal, sqlbuilder.SQLExpression)):
1390 return value
1391 if isinstance(value, float):
1392 value = str(value)
1393 try:
1394 connection = state.connection or state.soObject._connection
1395 except AttributeError:
1396 pass
1397 else:
1398 if hasattr(connection, "decimalSeparator"):
1399 value = value.replace(connection.decimalSeparator, ".")
1400 try:
1401 return Decimal(value)
1402 except:
1403 raise validators.Invalid("expected a Decimal in the DecimalCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1405
1406 def from_python(self, value, state):
1407 if value is None:
1408 return None
1409 if isinstance(value, float):
1410 value = str(value)
1411 if isinstance(value, basestring):
1412 try:
1413 connection = state.connection or state.soObject._connection
1414 except AttributeError:
1415 pass
1416 else:
1417 if hasattr(connection, "decimalSeparator"):
1418 value = value.replace(connection.decimalSeparator, ".")
1419 try:
1420 return Decimal(value)
1421 except:
1422 raise validators.Invalid("can not parse Decimal value '%s' in the DecimalCol from '%s'" %
1423 (value, getattr(state, 'soObject', '(unknown)')), value, state)
1424 if isinstance(value, (int, long, Decimal, sqlbuilder.SQLExpression)):
1425 return value
1426 raise validators.Invalid("expected a Decimal in the DecimalCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1428
1429class SODecimalCol(SOCol):
1430
1431 def __init__(self, **kw):
1432 self.size = kw.pop('size', NoDefault)
1433 assert self.size is not NoDefault, "You must give a size argument"
1435 self.precision = kw.pop('precision', NoDefault)
1436 assert self.precision is not NoDefault, "You must give a precision argument"
1438 super(SODecimalCol, self).__init__(**kw)
1439
1440 def _sqlType(self):
1441 return 'DECIMAL(%i, %i)' % (self.size, self.precision)
1442
1443 def createValidators(self):
1444 return [DecimalValidator(name=self.name)] + super(SODecimalCol, self).createValidators()
1446
1447class DecimalCol(Col):
1448 baseClass = SODecimalCol
1449
1450class SOCurrencyCol(SODecimalCol):
1451
1452 def __init__(self, **kw):
1453 pushKey(kw, 'size', 10)
1454 pushKey(kw, 'precision', 2)
1455 super(SOCurrencyCol, self).__init__(**kw)
1456
1457class CurrencyCol(DecimalCol):
1458 baseClass = SOCurrencyCol
1459
1460
1461class DecimalStringValidator(DecimalValidator):
1462 def to_python(self, value, state):
1463 value = super(DecimalStringValidator, self).to_python(value, state)
1464 if self.precision and isinstance(value, Decimal):
1465 assert value < self.max, "Value must be less than %s" % int(self.max)
1467 value = value.quantize(self.precision)
1468 return value
1469
1470 def from_python(self, value, state):
1471 value = super(DecimalStringValidator, self).from_python(value, state)
1472 if isinstance(value, Decimal):
1473 if self.precision:
1474 assert value < self.max, "Value must be less than %s" % int(self.max)
1476 value = value.quantize(self.precision)
1477 value = value.to_eng_string()
1478 elif isinstance(value, (int, long)):
1479 value = str(value)
1480 return value
1481
1482class SODecimalStringCol(SOStringCol):
1483 def __init__(self, **kw):
1484 self.size = kw.pop('size', NoDefault)
1485 assert (self.size is not NoDefault) and (self.size >= 0), "You must give a size argument as a positive integer"
1487 self.precision = kw.pop('precision', NoDefault)
1488 assert (self.precision is not NoDefault) and (self.precision >= 0), "You must give a precision argument as a positive integer"
1490 kw['length'] = int(self.size) + int(self.precision)
1491 self.quantize = kw.pop('quantize', False)
1492 assert isinstance(self.quantize, bool), "quantize argument must be Boolean True/False"
1494 super(SODecimalStringCol, self).__init__(**kw)
1495
1496 def createValidators(self):
1497 if self.quantize:
1498 v = DecimalStringValidator(name=self.name,
1499 precision=Decimal(10) ** (-1 * int(self.precision)),
1500 max=Decimal(10) ** (int(self.size) - int(self.precision)))
1501 else:
1502 v = DecimalStringValidator(name=self.name, precision=0)
1503 return [v] + super(SODecimalStringCol, self).createValidators(dataType=Decimal)
1505
1506class DecimalStringCol(StringCol):
1507 baseClass = SODecimalStringCol
1508
1509
1510class BinaryValidator(SOValidator):
1511 """
1512 Validator for binary types.
1513
1514 We're assuming that the per-database modules provide some form
1515 of wrapper type for binary conversion.
1516 """
1517
1518 _cachedValue = None
1519
1520 def to_python(self, value, state):
1521 if value is None:
1522 return None
1523 try:
1524 connection = state.connection or state.soObject._connection
1525 except AttributeError:
1526 dbName = None
1527 binaryType = type(None)
1528 else:
1529 dbName = connection.dbName
1530 binaryType = connection._binaryType
1531 if isinstance(value, str):
1532 if dbName == "sqlite":
1533 value = connection.module.decode(value)
1534 return value
1535 if isinstance(value, (buffer, binaryType)):
1536 cachedValue = self._cachedValue
1537 if cachedValue and cachedValue[1] == value:
1538 return cachedValue[0]
1539 if isinstance(value, array):
1540 return value.tostring()
1541 return str(value)
1542 raise validators.Invalid("expected a string in the BLOBCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1544
1545 def from_python(self, value, state):
1546 if value is None:
1547 return None
1548 connection = state.connection or state.soObject._connection
1549 binary = connection.createBinary(value)
1550 self._cachedValue = (value, binary)
1551 return binary
1552
1553class SOBLOBCol(SOStringCol):
1554 def __init__(self, **kw):
1555
1556 if 'varchar' not in kw: kw['varchar'] = False
1557 super(SOBLOBCol, self).__init__(**kw)
1558
1559 def createValidators(self):
1560 return [BinaryValidator(name=self.name)] + super(SOBLOBCol, self).createValidators()
1562
1563 def _mysqlType(self):
1564 length = self.length
1565 varchar = self.varchar
1566 if length >= 2**24:
1567 return varchar and "LONGTEXT" or "LONGBLOB"
1568 if length >= 2**16:
1569 return varchar and "MEDIUMTEXT" or "MEDIUMBLOB"
1570 if length >= 2**8:
1571 return varchar and "TEXT" or "BLOB"
1572 return varchar and "TINYTEXT" or "TINYBLOB"
1573
1574 def _postgresType(self):
1575 return 'BYTEA'
1576
1577 def _mssqlType(self):
1578 if self.connection and self.connection.can_use_max_types():
1579 return 'VARBINARY(MAX)'
1580 else:
1581 return "IMAGE"
1582
1583class BLOBCol(StringCol):
1584 baseClass = SOBLOBCol
1585
1586
1587class PickleValidator(BinaryValidator):
1588 """
1589 Validator for pickle types. A pickle type is simply a binary type
1590 with hidden pickling, so that we can simply store any kind of
1591 stuff in a particular column.
1592
1593 The support for this relies directly on the support for binary for
1594 your database.
1595 """
1596
1597 def to_python(self, value, state):
1598 if value is None:
1599 return None
1600 if isinstance(value, unicode):
1601 dbEncoding = self.getDbEncoding(state, default='ascii')
1602 value = value.encode(dbEncoding)
1603 if isinstance(value, str):
1604 return pickle.loads(value)
1605 raise validators.Invalid("expected a pickle string in the PickleCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1607
1608 def from_python(self, value, state):
1609 if value is None:
1610 return None
1611 return pickle.dumps(value, self.pickleProtocol)
1612
1613class SOPickleCol(SOBLOBCol):
1614
1615 def __init__(self, **kw):
1616 self.pickleProtocol = kw.pop('pickleProtocol', pickle.HIGHEST_PROTOCOL)
1617 super(SOPickleCol, self).__init__(**kw)
1618
1619 def createValidators(self):
1620 return [PickleValidator(name=self.name,
1621 pickleProtocol=self.pickleProtocol)] + super(SOPickleCol, self).createValidators()
1623
1624 def _mysqlType(self):
1625 length = self.length
1626 if length >= 2**24:
1627 return "LONGBLOB"
1628 if length >= 2**16:
1629 return "MEDIUMBLOB"
1630 return "BLOB"
1631
1632class PickleCol(BLOBCol):
1633 baseClass = SOPickleCol
1634
1635
1636def pushKey(kw, name, value):
1637 if not name in kw:
1638 kw[name] = value
1639
1640all = []
1641for key, value in globals().items():
1642 if isinstance(value, type) and (issubclass(value, (Col, SOCol))):
1643 all.append(key)
1644__all__.extend(all)
1645del all