SQLAlchemy默认日期时间

这是我的声明性模型:

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'

如果我使用Integer类型,我可以设置默认值。这是怎么呢

329484 次浏览

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)

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)

你也可以对默认的DateTime使用sqlalchemy内置函数

from sqlalchemy.sql import func


DT = Column(DateTime(timezone=True), default=func.now())

你可能想要使用onupdate=datetime.now,这样UPDATEs也会改变last_updated字段。

对于python执行的函数,SQLAlchemy有两个默认值。

  • default在INSERT上设置值,只设置一次
  • onupdate也将该值设置为UPDATE时的可调用的结果。

在数据库中计算时间戳,而不是在客户端中

为了正常起见,你可能希望所有datetimes都由你的DB服务器计算,而不是应用服务器。在应用程序中计算时间戳可能会导致问题,因为网络延迟是可变的,客户机的时钟漂移略有不同,不同的编程语言偶尔计算时间也略有不同。

SQLAlchemy允许您通过传递func.now()func.current_timestamp()(它们是彼此的别名)来做到这一点,它们告诉DB计算时间戳本身。

使用SQLALchemy的server_default

此外,对于已经告诉DB计算值的默认值,通常最好使用server_default而不是default。这告诉SQLAlchemy将默认值作为CREATE TABLE语句的一部分传递。

例如,如果你针对这个表编写一个临时脚本,使用server_default意味着你不需要担心手动向脚本添加时间戳调用——数据库将自动设置它。

理解SQLAlchemy的onupdate/server_onupdate

SQLAlchemy还支持onupdate,这样每当行更新时,它都会插入一个新的时间戳。同样,最好告诉DB自己计算时间戳:

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,当发生更新时,数据库将更改该列(也许您创建了列上的触发器),因此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文档中提供。不过,您需要自己提供适当的特定于驱动程序的函数。

根据PostgreSQL的文档:

nowCURRENT_TIMESTAMPLOCALTIMESTAMP返回事务的时间

这被认为是一个特性:目的是允许单个 交易要对“当前”有一个一致的概念;时间,所以 同一个事务中的多个修改具有相同的时间戳

如果你不想要事务时间戳,你可能想要使用statement_timestampclock_timestamp

statement_timestamp()

返回当前语句的开始时间(更具体地说, 从客户端接收最新命令消息的时间)。 statement_timestamp < / p >

clock_timestamp()

返回实际的当前时间,因此它的值甚至会改变

使用default参数和datetime.now:

from sqlalchemy import Column, Integer, DateTime
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)

Jeff Widman在他的回答中说过,你需要为func.utcnow()创建自己的UTC时间戳实现

因为我不想自己实现它,我已经搜索并找到了一个python包,它已经完成了这项工作,并由许多人维护。

包名是spoqa / sqlalchemy-ut

这个包的功能总结如下: 长话短说,UtcDateTime做:

take only aware datetime.datetime, return only aware datetime.datetime, 从不取或返回naive datetime.datetime, 确保数据库中的时间戳始终以UTC编码,并且

对于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文档中,建议从sqlalchemy本身导入text,并使用text设置server_default,插入自定义命令。

updated_at=Column(DateTime, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"))

要理解func.now,你可以阅读sql alchemy文档。

希望我能帮上忙。

注意,要使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()不会产生任何影响。