表示SQLAlchemy中

时间:2017-04-04 16:09:06

标签: python sql-server sqlalchemy

我正在尝试创建一些SQLAlchemy模型,并且正在努力将timedelta正确应用于特定列。 timedelta(以天为单位指定)作为整数存储在单独的表(Shifts)中,并且对于Exam表中的每条记录可能有所不同。

如果我使用hybrid_property,我可以使用移位日期计算属性

from sqlalchemy import Column, DateTime, Integer, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.ext.associationproxy import association_proxy


class Exam(Base):
    __table_args__ = {'schema': 'database.dbo'}

    PT_ID = Column(Integer, primary_key=True)

    # Unshifted date
    START_DTTM = Column(DateTime)

    shiftlink = relationship('Shifts', uselist=False)

    # I want the DATE_ADD attribute to be easily accessible here
    DATE_ADD = association_proxy('shiftlink', 'DATE_ADD')

    @hybrid_property
    def START_SHIFTED(self):
        return self.START_DTTM + timedelta(days=self.DATE_ADD)


class Shifts(Base):
    __table_args__ = {'schema': 'otherdb.dbo'}

    PT_ID = Column(Integer, ForeignKey(Exam.PT_ID))

    # Date shift in days (stored as an integer)
    DATE_ADD = Column(Integer)

但是,我希望能够根据此转移日期过滤我的查询。

如果我按照上面的说法尝试这样做,我会收到以下错误

  

TypeError:一元的坏操作数类型 - :'AssociationProxy'

为了解决此问题,我尝试为混合属性

定义以下expression
@START_SHIFTED.expression
def START_SHIFTED(cls):
    return func.dateadd(text('day'), cls.DATE_ADD, cls.START_DTTM)

当我尝试使用此过滤器时,实际查询看起来种类正确,除了JOIN表没有Shifts

from datetime import datetime
query = session.query(Exam).filter(Exam.START_SHIFTED < datetime.now())

print query
# SELECT ...
# FROM [database].[dbo].[exam]
# WHERE dateadd(day, %(dateadd_1)s, [database].[dbo].[exam].[BEGIN_DTTM]) < $(dateadd_2)s

但是当我尝试运行查询时,我收到以下错误:

query.count()
  

TypeError:未定义此子句的布尔值

我觉得我必须在这里错过一些简单的东西。我基本上只希望移位日期的行为就像datetime对象的内置支持一样。我应该使用column_property以外的AssociationProxy来定义Exam.DATE_ADD吗?

对于它的价值,我的数据库引擎是SQL Server 2012。

1 个答案:

答案 0 :(得分:0)

为了解决这个问题,我最终创建了types.DateTime类型的版本,该版本重载了bind_expressioncolumn_expression方法,这些方法应用了日期转换。此外,我必须创建一个自定义构造函数,该构造函数接受包含日期转换的列作为输入,然后将其存储起来以便在column_expressionbind_expression方法中使用。

此自定义类型会将转移应用于&#34; decode&#34;从数据库接收的值,并在执行任何类型的过滤时减去班次,以确保一致地应用班次。

from sqlalchemy import types, func, text

class ShiftedDateTime(types.TypeDecorator):

    impl = types.DateTime

    def __init__(self, column, *args, **kwargs):
        self.offset = column
        super(ShiftedDateTime, self).__init__(*args, **kwargs)

    def bind_expression(self, value):
        if value is None:
            return None

        return func.dateadd(text('DAY'), self.offset, value)

    def column_expression(self, col):
        return func.dateadd(text('DAY'), -self.offset, col)

class Shifts(Base):
    PT_ID = Column(Integer, primary_key=True)

    # Date shift in days (stored as an integer)
    DATE_ADD = Column(Integer)


class Exam(Base):

    PT_ID = Column(Integer, ForeignKey(Shifts.PT_ID))

    # Shifted start time
    START_DTTM = Column(ShiftedDateTime(Shifts.DATE_ADD))

    shiftlink = relationship(Shifts)

此方法的唯一缺点是必须为任何查询执行显式JOIN,因为新类型需要DATE_ADD列。这显然是必要的,但是如果可以执行隐式连接则会很好。

session.query(Exam).join(Shifts).filter(Exam.START_DTTM < datetime.now())