考虑以下代码:
class Appointment(Base):
scheduled_date_utc = Column(DateTime) # Naive UTC
scheduled_date_timezone = Column(TimezoneType()) # TimezoneType is from sqlalchemy-utils
@property
def scheduled_date(self) -> datetime:
... (assembles scheduled_date_utc and
scheduled_date_timezone into a unified object)
@scheduled_date.setter
def scheduled_date(self, value: datetime):
... (splits up tz-aware datetime into naive UTC time,
and timezone column, and sets them separately)
不必太担心属性方法,但要了解它们采用一个Python值,然后必须将该Python值分成两个数据库列。
自然,我更愿意创建自己的列类型:
class Appointment(Base):
scheduled_date = Column(MyDatetimeAware())
问题在于scheduled_date
不仅是一列,还需要是多列。有没有办法我可以在SQLAlchemy中概括多列“数据类型”?
答案 0 :(得分:0)
您正在考虑hybrid properties
(请参阅docs)。这些可用于在SQL和python设置中表现出不同的行为,但也可用于预定义某些转换。我经常使用它们将UTC时间戳转换为本地时区。请注意,您定义属性1-3次。一次作为python属性,一次用于表示SQL的运行方式,另一次用于设置程序。
import pytz
from sqlalchemy.ext.hybrid import hybrid_property
class Appointment(Base):
scheduled_date_utc = Column(DateTime) # Naive UTC
scheduled_date_timezone = Column(TimezoneType()) # TimezoneType is from sqlalchemy-utils
@property
def scheduled_date(self) -> datetime:
# see https://stackoverflow.com/a/18646797/5015356
return self.scheduled_date_utc\
.replace(tzinfo=pytz.utc)\
.astimezone(pytz.timezone(self.scheduled_date_timezone))
@scheduled_date.expr
def scheduled_date(cls):
return func.timezone(cls.scheduled_date_timezone, cls.scheduled_date_utc)
要使该解决方案可重用,可以在__setattr__
周围用包装器编写一个mixin:
import pytz
class TimeZoneMixin:
def is_timezone_aware_attr(self, attr):
return hasattr(self, attr + '_utc') and hasattr(self, attr + '_timezone')
def __getattr__(self, attr):
"""
__getattr__ is only called as a last resort, if no other
matching columns exist
"""
if self.is_timezone_aware_attr(attr):
return func.timezone(getattr(self, attr + '_utc'),
getattr(self, attr + '_timezone'))
raise AttributeError()
def __setattr__(self, attr, value):
if self.is_timezone_aware_attr(attr):
setattr(self, attr + '_utc', value.astimezone(tzinfo=pytz.utc))
setattr(self, attr + '_utc', value.tzinfo)
raise AttributeError()
或使其仅使用一个共享的timezone
对象:
import pytz
class TimeZoneMixin:
timezone = Column(TimezoneType())
def is_timezone_aware_attr(self, attr):
return hasattr(self, attr + '_utc')
def __getattr__(self, attr):
"""
__getattr__ is only called as a last resort, if no other
matching columns exist
"""
if self.is_timezone_aware_attr(attr):
return func.timezone(getattr(self, attr + '_utc'), self.timezone)
raise AttributeError()
def __setattr__(self, attr, value):
if self.is_timezone_aware_attr(attr):
setattr(self, attr + '_utc', value.astimezone(tzinfo=pytz.utc))
self.timezone = value.tzinfo
raise AttributeError()
答案 1 :(得分:0)
在特定于时区的日期时间列中,可以将TIMESTAMP列类型与受支持的后端一起使用。通常,如果要使用实际代表多个列的字段,则可以使用composite column type。但是,这确实需要您分别指定列。