cqle: introduce 'date' and 'time' column types
PYTHON-245
This commit is contained in:
@@ -19,9 +19,8 @@ import re
|
|||||||
import six
|
import six
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from cassandra.cqltypes import DateType
|
from cassandra import util
|
||||||
from cassandra.encoder import cql_quote
|
from cassandra.cqltypes import DateType, SimpleDateType
|
||||||
|
|
||||||
from cassandra.cqlengine import ValidationError
|
from cassandra.cqlengine import ValidationError
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@@ -361,6 +360,10 @@ class Integer(Column):
|
|||||||
class TinyInt(Integer):
|
class TinyInt(Integer):
|
||||||
"""
|
"""
|
||||||
Stores an 8-bit signed integer value
|
Stores an 8-bit signed integer value
|
||||||
|
|
||||||
|
.. versionadded:: 2.6.0
|
||||||
|
|
||||||
|
requires C* 2.2+ and protocol v4+
|
||||||
"""
|
"""
|
||||||
db_type = 'tinyint'
|
db_type = 'tinyint'
|
||||||
|
|
||||||
@@ -368,6 +371,10 @@ class TinyInt(Integer):
|
|||||||
class SmallInt(Integer):
|
class SmallInt(Integer):
|
||||||
"""
|
"""
|
||||||
Stores a 16-bit signed integer value
|
Stores a 16-bit signed integer value
|
||||||
|
|
||||||
|
.. versionadded:: 2.6.0
|
||||||
|
|
||||||
|
requires C* 2.2+ and protocol v4+
|
||||||
"""
|
"""
|
||||||
db_type = 'smallint'
|
db_type = 'smallint'
|
||||||
|
|
||||||
@@ -466,35 +473,43 @@ class DateTime(Column):
|
|||||||
|
|
||||||
class Date(Column):
|
class Date(Column):
|
||||||
"""
|
"""
|
||||||
*Note: this type is overloaded, and will likely be changed or removed to accommodate distinct date type
|
Stores a simple date, with no time-of-day
|
||||||
in a future version*
|
|
||||||
|
|
||||||
Stores a date value, with no time-of-day
|
.. versionchanged:: 2.6.0
|
||||||
|
|
||||||
|
removed overload of Date and DateTime. DateTime is a drop-in replacement for legacy models
|
||||||
|
|
||||||
|
requires C* 2.2+ and protocol v4+
|
||||||
"""
|
"""
|
||||||
db_type = 'timestamp'
|
db_type = 'date'
|
||||||
|
|
||||||
def to_python(self, value):
|
|
||||||
if value is None:
|
|
||||||
return
|
|
||||||
if isinstance(value, datetime):
|
|
||||||
return value.date()
|
|
||||||
elif isinstance(value, date):
|
|
||||||
return value
|
|
||||||
try:
|
|
||||||
return datetime.utcfromtimestamp(value).date()
|
|
||||||
except TypeError:
|
|
||||||
return datetime.utcfromtimestamp(DateType.deserialize(value)).date()
|
|
||||||
|
|
||||||
def to_database(self, value):
|
def to_database(self, value):
|
||||||
value = super(Date, self).to_database(value)
|
value = super(Date, self).to_database(value)
|
||||||
if value is None:
|
if value is None:
|
||||||
return
|
return
|
||||||
if isinstance(value, datetime):
|
|
||||||
value = value.date()
|
|
||||||
if not isinstance(value, date):
|
|
||||||
raise ValidationError("{} '{}' is not a date object".format(self.column_name, repr(value)))
|
|
||||||
|
|
||||||
return int((value - date(1970, 1, 1)).total_seconds() * 1000)
|
# need to translate to int version because some dates are not representable in
|
||||||
|
# string form (datetime limitation)
|
||||||
|
d = value if isinstance(value, util.Date) else util.Date(value)
|
||||||
|
return d.days_from_epoch + SimpleDateType.EPOCH_OFFSET_DAYS
|
||||||
|
|
||||||
|
|
||||||
|
class Time(Column):
|
||||||
|
"""
|
||||||
|
Stores a timezone-naive time-of-day, with nanosecond precision
|
||||||
|
|
||||||
|
.. versionadded:: 2.6.0
|
||||||
|
|
||||||
|
requires C* 2.2+ and protocol v4+
|
||||||
|
"""
|
||||||
|
db_type = 'time'
|
||||||
|
|
||||||
|
def to_database(self, value):
|
||||||
|
value = super(Time, self).to_database(value)
|
||||||
|
if value is None:
|
||||||
|
return
|
||||||
|
# str(util.Time) yields desired CQL encoding
|
||||||
|
return value if isinstance(value, util.Time) else util.Time(value)
|
||||||
|
|
||||||
|
|
||||||
class UUID(Column):
|
class UUID(Column):
|
||||||
@@ -852,7 +867,7 @@ class UserDefinedType(Column):
|
|||||||
|
|
||||||
def __init__(self, user_type, **kwargs):
|
def __init__(self, user_type, **kwargs):
|
||||||
"""
|
"""
|
||||||
:param type user_type: specifies the :class:`~.UserType` model of the column
|
:param type user_type: specifies the :class:`~.cqlengine.usertype.UserType` model of the column
|
||||||
"""
|
"""
|
||||||
self.user_type = user_type
|
self.user_type = user_type
|
||||||
self.db_type = "frozen<%s>" % user_type.type_name()
|
self.db_type = "frozen<%s>" % user_type.type_name()
|
||||||
|
@@ -638,27 +638,29 @@ class SimpleDateType(_CassandraType):
|
|||||||
typename = 'date'
|
typename = 'date'
|
||||||
date_format = "%Y-%m-%d"
|
date_format = "%Y-%m-%d"
|
||||||
|
|
||||||
|
# Values of the 'date'` type are encoded as 32-bit unsigned integers
|
||||||
|
# representing a number of days with epoch (January 1st, 1970) at the center of the
|
||||||
|
# range (2^31).
|
||||||
|
EPOCH_OFFSET_DAYS = 2 ** 31
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate(cls, val):
|
def validate(cls, val):
|
||||||
if not isinstance(val, util.Date):
|
if not isinstance(val, util.Date):
|
||||||
val = util.Date(val)
|
val = util.Date(val)
|
||||||
return val
|
return val
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def deserialize(byts, protocol_version):
|
||||||
|
days = uint32_unpack(byts) - SimpleDateType.EPOCH_OFFSET_DAYS
|
||||||
|
return util.Date(days)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def serialize(val, protocol_version):
|
def serialize(val, protocol_version):
|
||||||
# Values of the 'date'` type are encoded as 32-bit unsigned integers
|
|
||||||
# representing a number of days with epoch (January 1st, 1970) at the center of the
|
|
||||||
# range (2^31).
|
|
||||||
try:
|
try:
|
||||||
days = val.days_from_epoch
|
days = val.days_from_epoch
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
days = util.Date(val).days_from_epoch
|
days = util.Date(val).days_from_epoch
|
||||||
return uint32_pack(days + 2 ** 31)
|
return uint32_pack(days + SimpleDateType.EPOCH_OFFSET_DAYS)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def deserialize(byts, protocol_version):
|
|
||||||
days = uint32_unpack(byts) - 2 ** 31
|
|
||||||
return util.Date(days)
|
|
||||||
|
|
||||||
|
|
||||||
class ShortType(_CassandraType):
|
class ShortType(_CassandraType):
|
||||||
@@ -682,6 +684,10 @@ class TimeType(_CassandraType):
|
|||||||
val = util.Time(val)
|
val = util.Time(val)
|
||||||
return val
|
return val
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def deserialize(byts, protocol_version):
|
||||||
|
return util.Time(int64_unpack(byts))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def serialize(val, protocol_version):
|
def serialize(val, protocol_version):
|
||||||
try:
|
try:
|
||||||
@@ -690,10 +696,6 @@ class TimeType(_CassandraType):
|
|||||||
nano = util.Time(val).nanosecond_time
|
nano = util.Time(val).nanosecond_time
|
||||||
return int64_pack(nano)
|
return int64_pack(nano)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def deserialize(byts, protocol_version):
|
|
||||||
return util.Time(int64_unpack(byts))
|
|
||||||
|
|
||||||
|
|
||||||
class UTF8Type(_CassandraType):
|
class UTF8Type(_CassandraType):
|
||||||
typename = 'text'
|
typename = 'text'
|
||||||
|
@@ -917,7 +917,7 @@ class Time(object):
|
|||||||
|
|
||||||
class Date(object):
|
class Date(object):
|
||||||
'''
|
'''
|
||||||
Idealized naive date: year, month, day
|
Idealized date: year, month, day
|
||||||
|
|
||||||
Offers wider year range than datetime.date. For Dates that cannot be represented
|
Offers wider year range than datetime.date. For Dates that cannot be represented
|
||||||
as a datetime.date (because datetime.MINYEAR, datetime.MAXYEAR), this type falls back
|
as a datetime.date (because datetime.MINYEAR, datetime.MAXYEAR), this type falls back
|
||||||
@@ -997,5 +997,5 @@ class Date(object):
|
|||||||
dt = datetime_from_timestamp(self.seconds)
|
dt = datetime_from_timestamp(self.seconds)
|
||||||
return "%04d-%02d-%02d" % (dt.year, dt.month, dt.day)
|
return "%04d-%02d-%02d" % (dt.year, dt.month, dt.day)
|
||||||
except:
|
except:
|
||||||
# If we overflow datetime.[MIN|M
|
# If we overflow datetime.[MIN|MAX]
|
||||||
return str(self.days_from_epoch)
|
return str(self.days_from_epoch)
|
||||||
|
@@ -52,7 +52,7 @@ Columns of all types are initialized by passing :class:`.Column` attributes to t
|
|||||||
|
|
||||||
.. autoclass:: Counter
|
.. autoclass:: Counter
|
||||||
|
|
||||||
.. autoclass:: Date(**kwargs)
|
.. autoclass:: Date
|
||||||
|
|
||||||
.. autoclass:: DateTime(**kwargs)
|
.. autoclass:: DateTime(**kwargs)
|
||||||
|
|
||||||
@@ -70,10 +70,16 @@ Columns of all types are initialized by passing :class:`.Column` attributes to t
|
|||||||
|
|
||||||
.. autoclass:: Set
|
.. autoclass:: Set
|
||||||
|
|
||||||
|
.. autoclass:: SmallInt
|
||||||
|
|
||||||
.. autoclass:: Text
|
.. autoclass:: Text
|
||||||
|
|
||||||
|
.. autoclass:: Time
|
||||||
|
|
||||||
.. autoclass:: TimeUUID(**kwargs)
|
.. autoclass:: TimeUUID(**kwargs)
|
||||||
|
|
||||||
|
.. autoclass:: TinyInt
|
||||||
|
|
||||||
.. autoclass:: UserDefinedType
|
.. autoclass:: UserDefinedType
|
||||||
|
|
||||||
.. autoclass:: UUID(**kwargs)
|
.. autoclass:: UUID(**kwargs)
|
||||||
|
Reference in New Issue
Block a user