如何在SQLAlchemy中创建混合属性判断日期是否为节假日(规避Numpy转换)
解决SQLAlchemy混合属性判断节假日的问题
你的代码目前有两个核心问题:一是字段名不匹配(模型里定义的是date_of_usage,但代码里用了usage_date),二是表达式方法(@is_holiday.expression)里用了Python层面的in操作和numpy类型转换——这没法转换成数据库能执行的SQL语句,SQLAlchemy的表达式方法必须生成数据库可理解的逻辑。
下面是修正后的完整方案:
1. 优化实例方法(针对单个模型实例)
实例方法可以直接用Python+pandas的逻辑判断,不需要复杂的类型转换,同时修正字段名:
import pandas as pd import datetime as dt from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy import Column, Integer, DATE, Float from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class ElUsage(Base): __tablename__ = 'el_usage' # 记得添加上表名 recid = Column(Integer(), primary_key=True) date_of_usage = Column(DATE()) total_units = Column(Float(precision=5)) @hybrid_property def is_holiday(self): # 初始化日历(这里用美国联邦假日日历,你可以替换成自定义日历) cal = pd.tseries.holiday.USFederalHolidayCalendar() # 获取指定时间范围的节假日索引 holidays = cal.holidays(start=dt.date(2015, 1, 1), end=dt.date(2020, 12, 31)) # 直接判断日期是否在节假日列表中,返回1或0 return 1 if pd.to_datetime(self.date_of_usage) in holidays else 0
2. 正确实现表达式方法(针对SQL查询)
表达式方法需要生成数据库能执行的SQL切换,比较手wise操作里ASS负责Ch会 SC<要把节假日列表转换成SQL支持的日期常量,用CASE语句实现逻辑:
@is_holiday.expression def is_holiday(cls): from sqlalchemy import case cal = pd.tseries.holiday.USFederalHolidayCalendar() holidays = cal.holidays(start=dt.date(2015, 1, 1), end=dt.date(2020, 12, 31)) # 把pandas的DatetimeIndex转换成Python原生date对象列表 holiday_dates = [date.date() for date in holidays.to_pydatetime()] # 生成SQL的CASE表达式:如果日期在节假日列表里返回1,否则返回0 return case( [(cls.date_of_usage.in_(holiday_dates), 1)], else_=0 )
关键说明:
- 实例方法是给单个模型对象用的(比如
el_usage_instance.is_holiday),可以自由使用Python和pandas的逻辑。 - 表达式方法是给SQL查询用的(比如
session.query(ElUsage).filter(ElUsage.is_holiday == 1)),必须生成数据库能解析的SQL——这里的case和in_会被SQLAlchemy转换成类似CASE WHEN date_of_usage IN ('2015-01-01', '2015-01-19', ...) THEN 1 ELSE 0 END的语句,数据库可以直接执行。 - 如果你的节假日范围非常大,直接用
IN子句可能有性能问题,这时候可以考虑把节假日存到单独的数据库表中,用JOIN或子查询来判断,但2015-2020的节假日数量很少,完全不用在意。
内容的提问来源于stack exchange,提问作者Jaganadh Gopinadhan
相关产品推荐
相关产品推荐

