247

这是我的声明模型:

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'

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

4

10 回答 10

566

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

出于理智,您可能希望全部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,当更新发生时,您的数据库将更改列(也许您在列上创建了触发器),因此 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 时间戳,SQLAlchemy 文档func.utcnow()中提供了一个实现的存根。不过,您需要自己提供适当的驱动程序特定功能。

于 2015-11-04T21:15:47.380 回答
246

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)
于 2012-11-13T23:01:29.300 回答
76

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

from sqlalchemy.sql import func

DT = Column(DateTime(timezone=True), default=func.now())
于 2015-05-06T17:17:25.300 回答
13

您可能希望使用onupdate=datetime.nowUPDATE 来更改last_updated字段。

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

  • default在 INSERT 上设置值,仅一次
  • onupdate也将值设置为 UPDATE 上的可调用结果。
于 2015-07-17T21:24:01.330 回答
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)
于 2012-11-13T23:01:17.677 回答
5

使用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)                                                            
于 2021-05-07T06:09:50.720 回答
1

根据PostgreSQL 文档

now, CURRENT_TIMESTAMP,LOCALTIMESTAMP返回交易时间

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

您可能想要使用statement_timestamp或者clock_timestamp如果您不想要事务时间戳。

statement_timestamp()

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

clock_timestamp()

返回实际的当前时间,因此即使在单个 SQL 命令中,它的值也会发生变化。

于 2018-02-19T13:12:29.243 回答
1

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

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

要了解func.now,您可以阅读 sql alchemy 文档。

希望我在某种程度上有所帮助。

于 2021-09-26T20:00:32.637 回答
0

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

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

包名称是spoqa/sqlalchemy-ut

包的作用总结是:长话短说,UtcDateTime 做了:

只接受有意识datetime.datetime,只返回有意识datetime.datetime,从不接受或返回 naive datetime.datetime,确保数据库中的时间戳始终以 UTC 编码,并按预期工作。

于 2021-07-30T09:27:08.263 回答
0

请注意 forserver_default=func.now()func.now()to 工作:

Local_modified = Column(DateTime, server_default=func.now(), onupdate=func.now())

你需要DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP在你的表中设置 DDL。

例如

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()不会产生任何影响。

于 2021-09-29T15:49:26.733 回答