是否在执行查询前将SQLAlChemy模型中的DATETIME转换为Unix时间戳? [英] Convert datetime to unix timestamp in SQLAlchemy model before executing query?

查看:18
本文介绍了是否在执行查询前将SQLAlChemy模型中的DATETIME转换为Unix时间戳?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用SQLAlChemy来处理一个远程数据库,该数据库使用一种奇怪的时间戳格式--它将时间戳存储为自纪元以来的双精度毫秒。我想使用Python DateTime对象,所以我在模型中编写了getter/setter方法,如下所示this gist

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import synonym
from sqlalchemy.dialects.mysql import DOUBLE
import datetime

Base = declarative_base()
class Table(Base):
    __tablename__ = "table"

    id = Column(Integer, primary_key=True)
    _timestamp = Column("timestamp", DOUBLE(asdecimal=False))

    @property
    def timestamp(self):
        return datetime.datetime.utcfromtimestamp(float(self._timestamp)/1000.)

    @timestamp.setter
    def timestamp(self, dt):
        self._timestamp = float(dt.strftime("%s"))*1000.

    timestamp = synonym('_timestamp', descriptor=timestamp)

这对于向表中插入新行和使用表中的对象非常有用:

>>> table = session.query(Table).first()
<Table id=1>
>>> table.timestamp
datetime.datetime(2016, 6, 27, 16, 9, 3, 320000)
>>> table._timestamp
1467043743320.0

但是,当我尝试在筛选器表达式中使用日期时间时,它会崩溃:

>>> july = datetime.datetime(2016, 7, 1)
>>> old = session.query(Table).filter(Table.timestamp < july).first()
/lib/python2.7/site-packages/sqlalchemy/engine/default.py:450: Warning: Truncated incorrect DOUBLE value: '2016-07-01 00:00:00'
>>> july_flt = float(july.strftime("%s"))*1000.
>>> old = session.query(Table).filter(Table.timestamp < july_flt).first()
<Table id=1>

我认为这是因为我的getter/setter方法应用于TABLE类的实例,但不更改类本身的行为。我尝试使用混合属性而不是同义词进行重写:

from sqlalchemy.ext.hybrid import hybrid_property

class Table(Base):
    __tablename__ = "table"

    id = Column(Integer, primary_key=True)
    _timestamp = Column("timestamp", DOUBLE(asdecimal=False))

    @hybrid_property
    def timestamp(self):
        return datetime.datetime.utcfromtimestamp(float(self._timestamp)/1000.)

    @timestamp.setter
    def timestamp(self, dt):
        self._timestamp = float(dt.strftime("%s"))*1000.

同样,这对表实例有效,但在查询上失败--现在当我运行查询时,它命中了我的getter方法:

>>> july = datetime.datetime(2016, 7, 1)
>>> old = session.query(Table).filter(Table.timestamp < july).first()
Traceback:
  File "models.py", line 42, in timestamp
    return datetime.datetime.utcfromtimestamp(float(self._timestamp)/1000.)
TypeError: float() argument must be a string or a number

使用调试器,我可以看到getter正在接收Table._Timestamp类(不是特定的Table._Timestamp,也不是‘七月’)。

我看到我可以使用hybrid_property.expression修饰符来定义将时间戳转换为日期时间的SQL表达式,但我真正想要的是在python端将日期时间转换为时间戳,然后使用时间戳运行查询。换句话说,我希望在任何地方都使用DateTime(包括在查询中),但在SQL端使用微秒时间戳。我该怎么做?

推荐答案

您必须使用自定义类型,这并不像听起来那么可怕。

from sqlalchemy.types import TypeDecorator


class DoubleTimestamp(TypeDecorator):
    impl = DOUBLE

    def __init__(self):
        TypeDecorator.__init__(self, as_decimal=False)

    def process_bind_param(self, value, dialect):
        return value.replace(tzinfo=datetime.timezone.utc).timestamp() * 1000

    def process_result_value(self, value, dialect):
        return datetime.datetime.utcfromtimestamp(value / 1000)

Table变为:

class Table(Base):
    __tablename__ = "table"

    id = Column(Integer, primary_key=True)
    timestamp = Column(DoubleTimestamp)

然后你提到的一切都起作用了。您插入、选择并与datetime%s进行比较,但它存储为DOUBLE

这里我使用了不同的逻辑在时间戳之间进行转换,因为strftime('%s')不是正确的解决方案。这是一个不同的问题,已经answered correctly here。哦,我注意到你在发布的代码中说了微秒,但只转换为毫秒,除非这是口误😉。

这篇关于是否在执行查询前将SQLAlChemy模型中的DATETIME转换为Unix时间戳?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆