Merge branch 'master' of github.com:kvesteri/sqlalchemy-utils

This commit is contained in:
Ryan Leckey
2013-07-26 01:40:14 -07:00
6 changed files with 137 additions and 1 deletions

View File

@@ -191,6 +191,24 @@ or a 16-byte BINARY column or a 32-character CHAR column if not.
id = sa.Column(UUIDType(binary=False), primary_key=True)
TimezoneType
------------
TimezoneType provides a way for saving timezones (from either the pytz or the dateutil package) objects into database.
TimezoneType saves timezone objects as strings on the way in and converts them back to objects when querying the database.
::
from sqlalchemy_utils import UUIDType
class User(Base):
__tablename__ = 'user'
# Pass backend='pytz' to change it to use pytz (dateutil by default)
timezone = sa.Column(TimezoneType(backend='pytz'))
API Documentation
-----------------

View File

@@ -42,7 +42,8 @@ extras_require = {
],
'password': ['passlib >= 1.6, < 2.0'],
'color': ['colour>=0.0.4'],
'ipaddress': ['ipaddr'] if not PY3 else []
'ipaddress': ['ipaddr'] if not PY3 else [],
'timezone': ['python-dateutil']
}

View File

@@ -22,6 +22,7 @@ from .types import (
NumberRangeType,
ScalarListType,
ScalarListException,
TimezoneType,
TSVectorType,
UUIDType,
)
@@ -58,6 +59,7 @@ __all__ = (
ProxyDict,
ScalarListType,
ScalarListException,
TimezoneType,
TSVectorType,
UUIDType,
)

View File

@@ -15,6 +15,7 @@ from .number_range import (
from .password import Password, PasswordType
from .phone_number import PhoneNumber, PhoneNumberType
from .scalar_list import ScalarListException, ScalarListType
from .timezone import TimezoneType
from .uuid import UUIDType
@@ -33,6 +34,7 @@ __all__ = (
PhoneNumberType,
ScalarListException,
ScalarListType,
TimezoneType,
UUIDType,
)

View File

@@ -0,0 +1,74 @@
import six
from sqlalchemy import types
from sqlalchemy_utils import ImproperlyConfigured
class TimezoneType(types.TypeDecorator):
"""
Changes Timezone objects to a string representation on the way in and
changes them back to Timezone objects on the way out.
"""
impl = types.CHAR(50)
python_type = None
def __init__(self, backend='dateutil'):
"""
:param backend: Whether to use 'dateutil' or 'pytz' for timezones.
"""
self.backend = backend
if backend == 'dateutil':
try:
from dateutil.tz import tzfile
from dateutil.zoneinfo import gettz
self.python_type = tzfile
self._to = gettz
self._from = lambda x: x._filename
except ImportError:
raise ImproperlyConfigured(
"'python-dateutil' is required to use the "
"'dateutil' backend for 'TimezoneType'"
)
elif backend == 'pytz':
try:
from pytz import tzfile, timezone
self.python_type = tzfile.DstTzInfo
self._to = timezone
self._from = six.text_type
except ImportError:
raise ImproperlyConfigured(
"'pytz' is required to use the 'pytz' backend "
"for 'TimezoneType'"
)
else:
raise ImproperlyConfigured(
"'pytz' or 'dateutil' are the backends supported for "
"'TimezoneType'"
)
def _coerce(self, value):
if value and not isinstance(value, self.python_type):
obj = self._to(value)
if obj is None:
raise ValueError("unknown time zone '%s'" % value)
return obj
return value
def coercion_listener(self, target, value, oldvalue, initiator):
return self._coerce(value)
def process_bind_param(self, value, dialect):
return self._from(self._coerce(value)) if value else None
def process_result_value(self, value, dialect):
return self._to(value) if value else None

39
tests/test_timezone.py Normal file
View File

@@ -0,0 +1,39 @@
from pytest import mark
import six
import sqlalchemy as sa
from sqlalchemy_utils.types import timezone
from tests import TestCase
try:
import dateutil
except ImportError:
dateutil = None
@mark.skipif('dateutil is None')
class TestTimezoneType(TestCase):
def create_models(self):
class Visitor(self.Base):
__tablename__ = 'document'
id = sa.Column(sa.Integer, primary_key=True)
timezone = sa.Column(timezone.TimezoneType)
def __repr__(self):
return 'Visitor(%r)' % self.id
self.Visitor = Visitor
def test_parameter_processing(self):
visitor = self.Visitor(
timezone=u'America/Los_Angeles'
)
self.session.add(visitor)
self.session.commit()
visitor = self.session.query(self.Visitor).filter_by(
timezone='America/Los_Angeles').first()
assert visitor is not None