diff options
| author | Barry Warsaw | 2014-09-21 16:07:40 -0400 |
|---|---|---|
| committer | Barry Warsaw | 2014-09-21 16:07:40 -0400 |
| commit | 0ad6dc0bf9f69f8245693b86ed2715effebf1fb3 (patch) | |
| tree | 2dc0359dc7c3a043c87a92697c5de7ef2b0ddee3 /src/mailman/database/types.py | |
| parent | b6bc505e45a2f1f4f99d7dd2cdd868d533270ee9 (diff) | |
| parent | c339f06cca6ddf1d28cde2614a94c2a0c905957a (diff) | |
| download | mailman-0ad6dc0bf9f69f8245693b86ed2715effebf1fb3.tar.gz mailman-0ad6dc0bf9f69f8245693b86ed2715effebf1fb3.tar.zst mailman-0ad6dc0bf9f69f8245693b86ed2715effebf1fb3.zip | |
Diffstat (limited to 'src/mailman/database/types.py')
| -rw-r--r-- | src/mailman/database/types.py | 107 |
1 files changed, 86 insertions, 21 deletions
diff --git a/src/mailman/database/types.py b/src/mailman/database/types.py index ba3d92df4..a6f0b32ca 100644 --- a/src/mailman/database/types.py +++ b/src/mailman/database/types.py @@ -23,43 +23,108 @@ from __future__ import absolute_import, print_function, unicode_literals __metaclass__ = type __all__ = [ 'Enum', + 'UUID', ] +import uuid -from storm.properties import SimpleProperty -from storm.variables import Variable +from sqlalchemy import Integer +from sqlalchemy.types import TypeDecorator, BINARY, CHAR +from sqlalchemy.dialects import postgresql -class _EnumVariable(Variable): - """Storm variable for supporting enum types. - - To use this, make the database column a INTEGER. +class Enum(TypeDecorator): + """ + Stores an integer-based Enum as an integer in the database, and converts it + on-the-fly. """ - def __init__(self, *args, **kws): - self._enum = kws.pop('enum') - super(_EnumVariable, self).__init__(*args, **kws) + impl = Integer - def parse_set(self, value, from_db): + def __init__(self, *args, **kw): + self.enum = kw.pop("enum") + TypeDecorator.__init__(self, *args, **kw) + + def process_bind_param(self, value, dialect): if value is None: return None - if not from_db: - return value - return self._enum(value) - def parse_get(self, value, to_db): + return value.value + + + def process_result_value(self, value, dialect): if value is None: return None - if not to_db: + return self.enum(value) + + + +class UUID(TypeDecorator): + """ + Stores a UUID in the database natively when it can and falls back to + a BINARY(16) or a CHAR(32) when it can't. + + :: + + from sqlalchemy_utils import UUIDType + import uuid + + class User(Base): + __tablename__ = 'user' + + # Pass `binary=False` to fallback to CHAR instead of BINARY + id = sa.Column(UUIDType(binary=False), primary_key=True) + """ + impl = BINARY(16) + + python_type = uuid.UUID + + def __init__(self, binary=True, native=True): + """ + :param binary: Whether to use a BINARY(16) or CHAR(32) fallback. + """ + self.binary = binary + self.native = native + + def load_dialect_impl(self, dialect): + if dialect.name == 'postgresql' and self.native: + # Use the native UUID type. + return dialect.type_descriptor(postgresql.UUID()) + + else: + # Fallback to either a BINARY or a CHAR. + kind = self.impl if self.binary else CHAR(32) + return dialect.type_descriptor(kind) + + @staticmethod + def _coerce(value): + if value and not isinstance(value, uuid.UUID): + try: + value = uuid.UUID(value) + + except (TypeError, ValueError): + value = uuid.UUID(bytes=value) + + return value + + def process_bind_param(self, value, dialect): + if value is None: return value - return value.value + if not isinstance(value, uuid.UUID): + value = self._coerce(value) + + if self.native and dialect.name == 'postgresql': + return str(value) -class Enum(SimpleProperty): - """Custom type for Storm supporting enums.""" + return value.bytes if self.binary else value.hex + + def process_result_value(self, value, dialect): + if value is None: + return value - variable_class = _EnumVariable + if self.native and dialect.name == 'postgresql': + return uuid.UUID(value) - def __init__(self, enum=None): - super(Enum, self).__init__(enum=enum) + return uuid.UUID(bytes=value) if self.binary else uuid.UUID(value) |
