From: Mike Bayer Date: Mon, 15 Nov 2010 14:12:45 +0000 (-0500) Subject: - add a few choice TypeDecorator recipes X-Git-Tag: rel_0_7b1~268 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d01a6f088779bdf0bc118fa3ad09dc12dbd1baf5;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - add a few choice TypeDecorator recipes --- diff --git a/doc/build/core/types.rst b/doc/build/core/types.rst index 89a84abc8d..ddb40d045f 100644 --- a/doc/build/core/types.rst +++ b/doc/build/core/types.rst @@ -264,6 +264,124 @@ to and from the database is required. :inherited-members: :show-inheritance: +A few key :class:`.TypeDecorator` recipes follow. + +Rounding Numerics +^^^^^^^^^^^^^^^^^ + +Some database connectors like those of SQL Server choke if a Decimal is passed with too +many decimal places. Here's a recipe that rounds them down:: + + from sqlalchemy.types import TypeDecorator, Numeric + from decimal import Decimal + + class SafeNumeric(TypeDecorator): + """Adds quantization to Numeric.""" + + impl = Numeric + + def __init__(self, *arg, **kw): + TypeDecorator.__init__(self, *arg, **kw) + self.quantize_int = -(self.impl.precision - self.impl.scale) + self.quantize = Decimal(10) ** self.quantize_int + + def process_bind_param(self, value, dialect): + if isinstance(value, Decimal) and \ + value.as_tuple()[2] < self.quantize_int: + value = value.quantize(self.quantize) + return value + +Backend-agnostic GUID Type +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Receives and returns Python uuid() objects. Uses the PG UUID type +when using Postgresql, CHAR(32) on other backends, storing them +in stringified hex format. Can be modified to store +binary in CHAR(16) if desired:: + + from sqlalchemy.types import TypeDecorator, CHAR + from sqlalchemy.dialects.postgresql import UUID + import uuid + + class GUID(TypeDecorator): + """Platform-independent GUID type. + + Uses Postgresql's UUID type, otherwise uses + CHAR(32), storing as stringified hex values. + + """ + impl = CHAR + + def load_dialect_impl(self, dialect): + if dialect.name == 'postgresql': + return dialect.type_descriptor(UUID()) + else: + return dialect.type_descriptor(CHAR(32)) + + def process_bind_param(self, value, dialect): + if value is None: + return value + elif dialect.name == 'postgresql': + return str(value) + else: + if not isinstance(value, uuid.UUID): + return "%x" % uuid.UUID(value) + else: + # hexstring + return "%x" % value + + def process_result_value(self, value, dialect): + if value is None: + return value + else: + return uuid.UUID(value) + +Marshal JSON Strings +^^^^^^^^^^^^^^^^^^^^^ + +This type uses ``simplejson`` to marshal Python data structures +to/from JSON. Can be modified to use Python's builtin json encoder. + +Note that the base type is not "mutable", meaning in-place changes to +the value will not be detected by the ORM - you instead would need to +replace the existing value with a new one to detect changes. +The subtype ``MutableJSONEncodedDict`` +adds "mutability" to allow this, but note that "mutable" types add +a significant performance penalty to the ORM's flush process:: + + from sqlalchemy.types import TypeDecorator, MutableType, VARCHAR + import simplejson + + class JSONEncodedDict(TypeDecorator): + """Represents an immutable structure as a json-encoded string. + + Usage:: + + JSONEncodedDict(255) + + """ + + impl = VARCHAR + + def process_bind_param(self, value, dialect): + if value is not None: + value = simplejson.dumps(value, use_decimal=True) + + return value + + def process_result_value(self, value, dialect): + if value is not None: + value = simplejson.loads(value, use_decimal=True) + return value + + class MutableJSONEncodedDict(MutableType, JSONEncodedDict): + """Adds mutability to JSONEncodedDict.""" + + def copy_value(self, value): + return simplejson.loads( + simplejson.dumps(value, use_decimal=True), + use_decimal=True) + Creating New Types ~~~~~~~~~~~~~~~~~~