我正在尝试创建一些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。
答案 0 :(得分:0)
为了解决这个问题,我最终创建了types.DateTime
类型的版本,该版本重载了bind_expression
和column_expression
方法,这些方法应用了日期转换。此外,我必须创建一个自定义构造函数,该构造函数接受包含日期转换的列作为输入,然后将其存储起来以便在column_expression
和bind_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())