Add support for a timezone type.
This commit is contained in:
3
setup.py
3
setup.py
@@ -42,7 +42,8 @@ extras_require = {
|
|||||||
],
|
],
|
||||||
'password': ['passlib >= 1.6, < 2.0'],
|
'password': ['passlib >= 1.6, < 2.0'],
|
||||||
'color': ['colour>=0.0.4'],
|
'color': ['colour>=0.0.4'],
|
||||||
'ipaddress': ['ipaddr'] if not PY3 else []
|
'ipaddress': ['ipaddr'] if not PY3 else [],
|
||||||
|
'timezone': ['python-dateutil']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
74
sqlalchemy_utils/types/timezone.py
Normal file
74
sqlalchemy_utils/types/timezone.py
Normal 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
39
tests/test_timezone.py
Normal 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 TestIPAddressType(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
|
Reference in New Issue
Block a user