SQLAlchemy 默认日期时间
Posted
技术标签:
【中文标题】SQLAlchemy 默认日期时间【英文标题】:SQLAlchemy default DateTime 【发布时间】:2012-11-02 10:43:45 【问题描述】:这是我的声明模型:
import datetime
from sqlalchemy import Column, Integer, DateTime
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Test(Base):
__tablename__ = 'test'
id = Column(Integer, primary_key=True)
created_date = DateTime(default=datetime.datetime.utcnow)
但是,当我尝试导入此模块时,我收到此错误:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "orm/models2.py", line 37, in <module>
class Test(Base):
File "orm/models2.py", line 41, in Test
created_date = sqlalchemy.DateTime(default=datetime.datetime.utcnow)
TypeError: __init__() got an unexpected keyword argument 'default'
如果我使用整数类型,我可以设置一个默认值。怎么回事?
【问题讨论】:
不应该使用这个。 utcnow 是 UTC 时区中的原始时间戳,但是,原始时间戳可能会在本地时区中解释。 我知道这个问题是很久以前提出的,但我认为您的答案应该更改为@Jeff Widman 提供的答案,因为当表类为时,另一个答案将使用“编译”时间日期时间定义与创建记录的时间。如果您是 AFK,那么至少此评论会警告其他人仔细检查每个问题的 cmets。 【参考方案1】:在您的数据库中计算时间戳,而不是在您的客户端中
出于理智,您可能希望所有datetimes
都由您的数据库服务器而不是应用程序服务器计算。在应用程序中计算时间戳可能会导致问题,因为网络延迟是可变的,客户端会遇到稍微不同的时钟漂移,并且不同的编程语言有时会稍微不同地计算时间。
SQLAlchemy 允许您通过传递 func.now()
或 func.current_timestamp()
(它们是彼此的别名)来执行此操作,这告诉数据库自己计算时间戳。
使用 SQLALchemy 的server_default
此外,对于您已经告诉数据库计算值的默认值,通常最好使用server_default
而不是default
。这告诉 SQLAlchemy 将默认值作为CREATE TABLE
语句的一部分传递。
例如,如果您针对此表编写临时脚本,使用 server_default
意味着您无需担心手动向脚本添加时间戳调用 - 数据库会自动设置它。
了解 SQLAlchemy 的 onupdate
/server_onupdate
SQLAlchemy 还支持onupdate
,因此只要行更新,它就会插入一个新的时间戳。同样,最好告诉数据库自己计算时间戳:
from sqlalchemy.sql import func
time_created = Column(DateTime(timezone=True), server_default=func.now())
time_updated = Column(DateTime(timezone=True), onupdate=func.now())
有一个server_onupdate
参数,但与server_default
不同,它实际上并没有在服务器端设置任何东西。它只是告诉 SQLalchemy,当更新发生时,您的数据库将更改列(也许您创建了一个 trigger on the column ),因此 SQLAlchemy 将要求返回值,以便它可以更新相应的对象。
另一个潜在的问题:
您可能会惊讶地发现,如果您在单个事务中进行大量更改,它们都具有相同的时间戳。这是因为 SQL 标准规定 CURRENT_TIMESTAMP
根据事务的开始返回值。
PostgreSQL 提供非 SQL 标准的 statement_timestamp()
和 clock_timestamp()
,确实在事务中更改。文档在这里:https://www.postgresql.org/docs/current/static/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT
UTC 时间戳
如果您想使用 UTC 时间戳,func.utcnow()
的实现存根在 SQLAlchemy documentation 中提供。不过,您需要自己提供适当的驱动程序特定功能。
【讨论】:
我确实看到现在有一种方法可以告诉 Postgres 使用当前时间,而不仅仅是事务开始时间......它在 postgres 文档中 有 func.utcnow() 之类的吗? @JeffWidman:我们有 utcnow 的 mysql 实现吗?我在文档中只有 mssql 和 postreg 这确实是一个很好的答案!server_default=func.now()
为我工作,但 onupdate=func.now()
没有。试过onupdate=datetime.datetime.utcnow
(不带括号),也没有帮助【参考方案2】:
DateTime
没有默认键作为输入。默认键应该是Column
函数的输入。试试这个:
import datetime
from sqlalchemy import Column, Integer, DateTime
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Test(Base):
__tablename__ = 'test'
id = Column(Integer, primary_key=True)
created_date = Column(DateTime, default=datetime.datetime.utcnow)
【讨论】:
这是不对的。模型加载时的时间戳将用于所有新记录,而不是添加记录的时间。 @SkyLeach 这个代码是正确的,你可以在这里测试它:pastebin.com/VLyWktUn。datetime.datetime.utcnow
似乎被称为回调。
我使用了这个答案,但模型加载时的时间戳正用于所有新记录。
@scottdelta:确保你没有使用default=datetime.datetime.utcnow()
;你想传递utcnow
函数,而不是在模块加载时评估它的结果。
仍然对我不起作用。 jeff-widman 的方法对我有用:from sqlalchemy.sql import func; created_date = Column(DateTime(timezone=True), server_default=func.now()
【参考方案3】:
您也可以使用 sqlalchemy 内置函数作为默认 DateTime
from sqlalchemy.sql import func
DT = Column(DateTime(timezone=True), default=func.now())
【讨论】:
您也可以使用server_default
代替default
,这样值将由数据库本身处理。
在定义描述符时不执行 func.now(),因此所有模型/子项(如果这是基类)将具有相同的 DT 值。通过传入像“datetime.datetime.utcnow”这样的函数引用,它会为每个函数单独执行。这对于“创建”或“更新”属性至关重要。
@Metalstorm 不,这是正确的。 sqlalchemy.sql.func 是一个特殊情况,它返回一个 sqlalchemy.sql.functions.now 实例,而不是当前时间。 docs.sqlalchemy.org/en/latest/core/…
timezone=True
是做什么的?
@self, From the documentation: "如果为真,并且受后端支持,将生成 'TIMESTAMP WITH TIMEZONE'。对于不支持时区感知时间戳的后端,没有效果。 .【参考方案4】:
您可能希望使用onupdate=datetime.now
,以便更新也更改last_updated
字段。
SQLAlchemy 有两个 Python 执行函数的默认值。
default
在 INSERT 上设置值,只设置一次
onupdate
也在 UPDATE 上将值设置为 callable 结果。
【讨论】:
我不知道为什么,但出于某种原因onupdate
没有为我做任何事情。
我终于通过这样做在 Postgres db 上工作了:created_at = db.Column(db.DateTime, server_default=UtcNow())
和 updated_at = db.Column(db.DateTime, server_default=UtcNow(), onupdate=UtcNow())
其中UtcNow
是class UtcNow(expression.FunctionElement): type = DateTime()
的类,我们有@compiles(UtcNow, 'postgresql') def pg_utc_now(element, compiler, **kw): return "TIMEZONE('utc', CURRENT_TIMESTAMP)"
我相信sqlalchmey.sql.func.now()
是首选,但不会有太大不同。如果系统是分布式的并且许多不同的用户可能在同一时间进行更改,那么使用 DB 时间而不是服务器时间会稍微好一些
有没有办法禁用对 python 执行函数的 onupdate 调用? docs.sqlalchemy.org/en/14/core/defaults.html
我做了同样的事情,但由于没有将任何 args 传递给 DB 模型中的 UtcNow,我得到了一个错误。【参考方案5】:
default
关键字参数应该给 Column 对象。
例子:
Column(u'timestamp', TIMESTAMP(timezone=True), primary_key=False, nullable=False, default=time_now),
默认值可以是可调用的,我在这里定义如下。
from pytz import timezone
from datetime import datetime
UTC = timezone('UTC')
def time_now():
return datetime.now(UTC)
【讨论】:
time_now
来自哪里?
谢谢!我假设有一个我以某种方式忽略的名称的内置函数。
或datetime.datetime.utcnow
from datetime import datetime as dt .... then just dt.utcnow()【参考方案6】:
将default
参数与datetime.now
一起使用:
from datetime import datetime
class Test(Base):
__tablename__ = 'test'
id = Column(Integer, primary_key=True)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
【讨论】:
请正确格式化您的代码,并解释一下它是如何解决问题的。【参考方案7】:根据PostgreSQL documentation:
now
,CURRENT_TIMESTAMP
,LOCALTIMESTAMP
返回交易时间
这被认为是一项功能:其目的是允许单个 事务具有一致的“当前”时间概念,因此 同一事务中的多个修改具有相同的时间戳。
如果您不想要交易时间戳,您可能想要使用 statement_timestamp
或 clock_timestamp
。
statement_timestamp()
返回当前语句的开始时间(更具体地说, 收到客户端最新命令消息的时间)。 statement_timestamp
clock_timestamp()
返回实际的当前时间,因此它的值甚至会改变 在单个 SQL 命令中。
【讨论】:
【参考方案8】:mariadb 这对我有用:
from sqlalchemy import Column, Integer, String, DateTime, TIMESTAMP, text
from sqlalchemy.sql import func
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Test(Base):
__tablename__ = "test"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(255), nullable=False)
email = Column(String(255), nullable=False)
created_at = Column(TIMESTAMP, nullable=False, server_default=func.now())
updated_at = Column(DateTime, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"))
在mariadb的sqlalchemy documentation中,建议从sqlalchemy本身导入text
,并将server_default
设置为text
,插入自定义命令。
updated_at=Column(DateTime, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"))
要了解func.now
,您可以阅读 sql alchemy 文档。
希望我能以某种方式提供帮助。
【讨论】:
【参考方案9】:Jeff Widman 在 his answer 上说,您需要为 func.utcnow()
创建自己的 UTC 时间戳实现
由于我不想自己实现它,所以我搜索并找到了一个 python 包,它已经完成了这项工作并由很多人维护。
包名是spoqa/sqlalchemy-ut。
这个包的作用总结是: 长话短说,UtcDateTime 确实:
只知道datetime.datetime
,
只返回知道datetime.datetime
,
永远不要天真地接受或返回datetime.datetime
,
确保数据库中的时间戳始终以 UTC 编码,并且
按照您的预期工作。
【讨论】:
【参考方案10】:请注意,server_default=func.now()
和 func.now()
工作:
Local_modified = Column(DateTime, server_default=func.now(), onupdate=func.now())
您需要在表格 DDL 中设置DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
。
例如
create table test
(
id int auto_increment
primary key,
source varchar(50) null,
Local_modified datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)
collate=utf8mb4_bin;
否则,server_default=func.now(), onupdate=func.now()
无效。
【讨论】:
以上是关于SQLAlchemy 默认日期时间的主要内容,如果未能解决你的问题,请参考以下文章
SQLAlchemy - 将日期时间设置为字符串并获取日期?
如何在 SqlAlchemy / Mysql 中存储时区感知日期时间值?