SQLAlchemy默认DateTime


174

这是我的声明性模型:

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'

如果使用整数类型,则可以设置默认值。这是怎么回事?


1
这不应该被使用。utcnow是UTC时区的天真时间戳,但是,天真时间戳有可能会在本地时区中解释。
Antti Haapala

1
我知道这个问题是很久以前问过的,但是我认为您的答案应该更改为@Jeff Widman提供的答案,因为在定义表类与创建记录时,另一个将使用“编译”日期时间。如果您是AFK,那么至少此注释将为其他人提供警告,请您仔细检查每个问题的注释。
大卫,

Answers:


186

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)

13
这不对。模型加载时的时间戳将用于所有新记录,而不是添加记录的时间。
SkyLeach

8
@SkyLeach此代码正确,可以在这里进行测试:pastebin.com/VLyWktUndatetime.datetime.utcnow似乎称为回调。
bux

1
我使用了这个答案,但是所有新记录都使用了模型加载时的时间戳。
scottydelta

105
@scottdelta:确保您没有使用default=datetime.datetime.utcnow();您要传递utcnow函数,而不是在模块加载时对其求值的结果。
功能障碍

仍然没有为我工作。杰夫·维德曼的方法对我from sqlalchemy.sql import func; created_date = Column(DateTime(timezone=True), server_default=func.now()
有用

396

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

为了理智,您可能希望datetimes由数据库服务器而不是应用程序服务器来计算所有数据。计算应用程序中的时间戳可能会导致问题,因为网络等待时间是可变的,客户端会经历略微不同的时钟漂移,并且不同的编程语言有时会略有不同地计算时间。

SQLAlchemy允许您通过传递func.now()func.current_timestamp()(它们是彼此的别名)来执行此操作,该命令告诉DB计算时间戳本身。

使用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更新发生时(也许您在列上创建了触发器),数据库将更改列,因此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文档中提供的实现存根。但是,您需要自己提供适当的特定于驱动程序的功能。


1
我确实看到有一种方法可以告诉Postgres使用当前时间,而不仅仅是交易开始时间...它在postgres文档中
Jeff Widman

2
是否有func.utcnow()或类似的东西?
yerassyl '17

1
@JeffWidman:我们是否有utcnow的mysql实现?我在文档中仅使用mssql和postreg
JavaSa

1
这确实是一个很好的答案!
约翰·穆图玛

3
server_default=func.now()为我工作,但onupdate=func.now()没有。尝试过onupdate=datetime.datetime.utcnow(没有括号),这也没有帮助
Vikas Prasad

55

您还可以默认使用sqlalchemy内置函数 DateTime

from sqlalchemy.sql import func

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

9
您也可以使用server_default代替default值,而是由数据库本身处理。
rgtk 2015年

2
定义描述符时不执行func.now(),因此所有模型/子级(如果这是基类)将具有相同的DT值。通过传入诸如“ datetime.datetime.utcnow”之类的函数引用,它将分别针对每个函数执行。这对于“创建”或“更新”属性至关重要。
Metalstorm '16

10
@Metalstorm不,这是正确的。sqlalchemy.sql.func是一种特殊情况,它返回sqlalchemy.sql.functions.now实例,而不是当前时间。 docs.sqlalchemy.org/en/latest/core/...
克里斯哈迪

怎么timezone=True办?
ChaimG '16

1
@self,从文档“如果属实,以及后台支持,会产生‘TIMESTAMP WITH TIMEZONE’对于不支持时区意识到时间戳后端,没有任何效果。
ChaimG

7

您可能想要使用,onupdate=datetime.now以便UPDATE也可以更改该last_updated字段。

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

  • default 设置一次INSERT的值
  • onupdate还将值设置为UPDATE 上的可调用结果。

我不知道为什么,但是出于某种原因onupdate对我没有任何帮助。
维卡斯·普拉萨德

1
我终于得到它通过这样的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)"
维卡斯·普拉萨德·

6

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来的?
凯-SE很邪恶

谢谢!我假设有一个内置函数,但我以某种方式忽略了该名称。
凯-SE很邪恶

datetime.datetime.utcnow
serkef

1

根据PostgreSQL文档,https://www.postgresql.org/docs/9.6/static/functions-datetime.html

now, CURRENT_TIMESTAMP, LOCALTIMESTAMP return the time of transaction.

这被认为是一个功能:目的是允许单个事务具有“当前”时间的一致概念,以便同一事务内的多个修改具有相同的时间戳。

如果您不希望事务时间戳记,则可能要使用statement_timestampclock_timestamp

statement_timestamp()

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

clock_timestamp()

返回实际的当前时间,因此,即使在单个SQL命令中,其值也会更改。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.