SqlAlchemy TIMESTAMP 'on update' 额外

Sim*_*ini 6 python mysql sqlalchemy python-3.x sql-timestamp

我在 python3.4.3 上使用 SqlAlchemy 来管理 MySQL 数据库。我正在创建一个表:

from datetime import datetime

from sqlalchemy import Column, text, create_engine
from sqlalchemy.types import TIMESTAMP
from sqlalchemy.dialects.mysql import BIGINT
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()
class MyClass(Base):

  __tablename__ = 'my_class'

  id = Column(BIGINT(unsigned=True), primary_key=True)
  created_at = Column(TIMESTAMP, default=datetime.utcnow, nullable=False)
  updated_at = Column(TIMESTAMP, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
  param1 = Column(BIGINT(unsigned=True), server_default=text('0'), nullable=False)
Run Code Online (Sandbox Code Playgroud)

当我创建这个表时:

engine = create_engine('{dialect}://{user}:{password}@{host}/{name}'.format(**utils.config['db']))
Base.metadata.create_all(engine)
Run Code Online (Sandbox Code Playgroud)

我得到:

mysql> describe my_class;
+----------------+---------------------+------+-----+---------------------+-----------------------------+
| Field          | Type                | Null | Key | Default             | Extra                       |
+----------------+---------------------+------+-----+---------------------+-----------------------------+
| id             | bigint(20) unsigned | NO   | PRI | NULL                | auto_increment              |
| created_at     | timestamp           | NO   |     | CURRENT_TIMESTAMP   | on update CURRENT_TIMESTAMP |                           |
| updated_at     | timestamp           | NO   |     | 0000-00-00 00:00:00 |                             |
| param1         | bigint(20) unsigned | NO   |     | 0                   |                             |
Run Code Online (Sandbox Code Playgroud)

现在的问题是我不希望我的created_at属性上有任何 on_update 服务器默认值,事实上,它的目的是只在创建记录时写入,而不是在每次更新时写入,如类声明中所述。

从我所做的几个测试中,我注意到如果我TIMESTAMP在 before插入另一个类型的属性created_at,那么这个属性会得到on update CURRENT_TIMESTAMP额外的,而created_at不会,如所愿。这表明TIMESTAMPSqlAlchemy 在映射声明中找到的第一个属性得到了on update CURRENT_TIMESTAMP额外的,尽管我没有看到这种行为的任何原因。

我也试过:

created_at = Column(TIMESTAMP, default=datetime.utcnow, server_onupdate=None, nullable=False)
Run Code Online (Sandbox Code Playgroud)

created_at = Column(TIMESTAMP, default=datetime.utcnow, server_onupdate=text(''), nullable=False)
Run Code Online (Sandbox Code Playgroud)

但问题仍然存在。有什么建议吗?

Sim*_*ini 6

显然问题与 SqlAlchemy 无关,而是与底层 MySQL 引擎有关。默认行为是on update CURRENT_TIMESTAMP在表中的第一个 TIMESTAMP 列上设置。

此处描述此行为。据我了解,一个可能的解决方案是使用该--explicit_defaults_for_timestamp=FALSE标志启动 MySQL 。另一个解决方案可以在这里找到。我还没有尝试过任何一种解决方案,我会在解决问题后立即更新此答案。

编辑:我尝试了第二种方法,它不是很方便,但它有效。在我的情况下,我创建了一组没有created_at属性然后按照上面链接中的描述更改了所有剩余的表。

类似的东西:

_no_alter = set(['tables', 'which', 'do not', 'have', 'a created_at', 'column'])
Base.metadata.create_all(engine)
for table in Base.metadata.tables.keys():
    if table not in _no_alter:
      engine.execute(text('ALTER TABLE {} MODIFY created_at TIMESTAMP NOT NULL DEFAULT 0'.format(table)))
Run Code Online (Sandbox Code Playgroud)

EDIT2:实现此目的的另一种(更简单)方法是在 SqlAlchemyserver_default中为该列设置一个值:

created_at = Column(TIMESTAMP, default=datetime.utcnow, nullable=False, server_default=text('0'))
Run Code Online (Sandbox Code Playgroud)


小智 6

我遇到了同样的问题,您可以通过以下方式完成此操作:

created_time   = Column(TIMESTAMP, nullable=False, server_default=func.now())
updated_time   = Column(TIMESTAMP, nullable=False, server_default=text('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'))
Run Code Online (Sandbox Code Playgroud)