SqlAlchemy TIMESTAMP 'on update' 额外
SqlAlchemy TIMESTAMP 'on update' extra
我在 python3.4.3 上使用 SqlAlchemy 来管理 MySQL 数据库。我正在创建一个 table 具有:
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)
当我创建此 table 时:
engine = create_engine('{dialect}://{user}:{password}@{host}/{name}'.format(**utils.config['db']))
Base.metadata.create_all(engine)
我得到:
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 | |
现在的问题是我不希望任何 on_update 服务器默认我的 created_at
属性,它的目的实际上是只在创建记录时写入,而不是在每次更新,如 class.
的声明中所述
从我所做的几个测试中,我注意到如果我在 created_at
之前插入另一个 TIMESTAMP
类型的属性,那么这个属性会得到额外的 on update CURRENT_TIMESTAMP
,而 created_at
没有,如所愿。这表明 SqlAlchemy 在映射声明中找到的第一个 TIMESTAMP
属性获得了 on update CURRENT_TIMESTAMP
额外的,尽管我没有看到这种行为的任何原因。
我也试过:
created_at = Column(TIMESTAMP, default=datetime.utcnow, server_onupdate=None, nullable=False)
和
created_at = Column(TIMESTAMP, default=datetime.utcnow, server_onupdate=text(''), nullable=False)
但问题依然存在。
有什么建议吗?
显然问题与 SqlAlchemy 无关,而与底层 MySQL 引擎有关。默认行为是在 table.
中的第一个 TIMESTAMP 列上设置 on update CURRENT_TIMESTAMP
描述了此行为 here. As far as I understand, a possible solution is to start MySQL with the --explicit_defaults_for_timestamp=FALSE
flag. Another solution can be found here。我还没有尝试过任何一种解决方案,我会在解决问题后立即更新此答案。
编辑:我尝试了第二种方法,虽然不是很方便,但很管用。在我的例子中,我创建了一组没有 created_at
属性的 table,然后我按照上面 link 中的描述更改了所有剩余的 table .
大致如下:
_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)))
EDIT2:另一种(更简单的)方法是在 SqlAlchemy 中为列设置一个 server_default
值:
created_at = Column(TIMESTAMP, default=datetime.utcnow, nullable=False, server_default=text('0'))
我遇到了同样的问题,您可以通过以下方式解决:
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'))
我在 python3.4.3 上使用 SqlAlchemy 来管理 MySQL 数据库。我正在创建一个 table 具有:
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)
当我创建此 table 时:
engine = create_engine('{dialect}://{user}:{password}@{host}/{name}'.format(**utils.config['db']))
Base.metadata.create_all(engine)
我得到:
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 | |
现在的问题是我不希望任何 on_update 服务器默认我的 created_at
属性,它的目的实际上是只在创建记录时写入,而不是在每次更新,如 class.
从我所做的几个测试中,我注意到如果我在 created_at
之前插入另一个 TIMESTAMP
类型的属性,那么这个属性会得到额外的 on update CURRENT_TIMESTAMP
,而 created_at
没有,如所愿。这表明 SqlAlchemy 在映射声明中找到的第一个 TIMESTAMP
属性获得了 on update CURRENT_TIMESTAMP
额外的,尽管我没有看到这种行为的任何原因。
我也试过:
created_at = Column(TIMESTAMP, default=datetime.utcnow, server_onupdate=None, nullable=False)
和
created_at = Column(TIMESTAMP, default=datetime.utcnow, server_onupdate=text(''), nullable=False)
但问题依然存在。 有什么建议吗?
显然问题与 SqlAlchemy 无关,而与底层 MySQL 引擎有关。默认行为是在 table.
中的第一个 TIMESTAMP 列上设置on update CURRENT_TIMESTAMP
描述了此行为 here. As far as I understand, a possible solution is to start MySQL with the --explicit_defaults_for_timestamp=FALSE
flag. Another solution can be found here。我还没有尝试过任何一种解决方案,我会在解决问题后立即更新此答案。
编辑:我尝试了第二种方法,虽然不是很方便,但很管用。在我的例子中,我创建了一组没有 created_at
属性的 table,然后我按照上面 link 中的描述更改了所有剩余的 table .
大致如下:
_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)))
EDIT2:另一种(更简单的)方法是在 SqlAlchemy 中为列设置一个 server_default
值:
created_at = Column(TIMESTAMP, default=datetime.utcnow, nullable=False, server_default=text('0'))
我遇到了同样的问题,您可以通过以下方式解决:
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'))