SQLAlchemy 2.0 ORM 模型日期时间插入

Rem*_*emi 12 python datetime sqlalchemy

我在created_date使用 SQLAlchemy 2.0 和 ORM 模型来获取专栏时遇到了一些真正的麻烦。到目前为止,我找到的最佳答案是在这个评论中:/sf/answers/2347250811/,但是我还无法使该功能正常工作。在我的(简化的)models.py文件中,我有:

\n
import datetime\nfrom sqlalchemy import Integer, String, DateTime\nfrom sqlalchemy.sql import func\nfrom sqlalchemy.orm import DeclarativeBase\nfrom sqlalchemy.orm import Mapped\nfrom sqlalchemy.orm import mapped_column\n\nclass Base(DeclarativeBase):\n    pass\n\nclass MyTable(Base):\n    __tablename__ = "my_table"\n    id: Mapped[int] = mapped_column(primary_key=True)\n    name: Mapped[str] = mapped_column(String, nullable=False)\n    created_date: Mapped[datetime.datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())\n
Run Code Online (Sandbox Code Playgroud)\n

到目前为止,一切都很好,我想。在简化版中,engine.py我有:

\n
from sqlalchemy import create_engine\nfrom sqlalchemy import select\nfrom sqlalchemy.orm import Session\n\nimport models\n\ndef add_entry(engine, name_str):\n    this_row = models.MyTable()\n    this_row.name = name_str\n    with Session(engine) as session:\n        session.add(this_row)\n        session.commit()\n
Run Code Online (Sandbox Code Playgroud)\n

如果我理解正确的话, 的默认值是created_date一个 SQL 函数,并且 SQLAlchemy 映射now()到 SQLite3 的datetime()。将引擎设置为echo=True,当它尝试运行此插入命令时,我得到以下结果(请注意,这是来自非简化形式的数据,但它仍然非常简单,有 3 个字符串而不是我描述的字符串)

\n
2023-02-06 09:47:07,080 INFO sqlalchemy.engine.Engine BEGIN (implicit)\n2023-02-06 09:47:07,080 INFO sqlalchemy.engine.Engine INSERT INTO coaches (d_name, bb2_name, bb3_name) VALUES (?, ?, ?) RETURNING id, created_date\n2023-02-06 09:47:07,081 INFO sqlalchemy.engine.Engine [generated in 0.00016s] (\'andy#1111\', \'AndyAnderson\', \'Killer Andy\')\n2023-02-06 09:47:07,081 INFO sqlalchemy.engine.Engine ROLLBACK\n
Run Code Online (Sandbox Code Playgroud)\n

当到达时间函数时,这会导致异常:IntegrityError: NOT NULL constraint failed: coaches.created_date

\n

一些额外的数据(我一直在使用rich产生大量调试信息的库,所以我试图获得最好的部分:

\n
\xe2\x94\x82 \xe2\x95\xad\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80 locals \xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x95\xae \xe2\x94\x82\n\xe2\x94\x82 \xe2\x94\x82    exc_tb = <traceback object at 0x00000108BD2565C0>                                         \xe2\x94\x82 \xe2\x94\x82\n\xe2\x94\x82 \xe2\x94\x82  exc_type = <class \'sqlalchemy.exc.IntegrityError\'>                                          \xe2\x94\x82 \xe2\x94\x82\n\xe2\x94\x82 \xe2\x94\x82 exc_value = IntegrityError(\'(sqlite3.IntegrityError) NOT NULL constraint failed:             \xe2\x94\x82 \xe2\x94\x82\n\xe2\x94\x82 \xe2\x94\x82             coaches.created_date\')                                                           \xe2\x94\x82 \xe2\x94\x82\n\xe2\x94\x82 \xe2\x94\x82      self = <sqlalchemy.util.langhelpers.safe_reraise object at 0x00000108BD1B79A0>          \xe2\x94\x82 \xe2\x94\x82\n\xe2\x94\x82 \xe2\x94\x82 traceback = None                                                                             \xe2\x94\x82 \xe2\x94\x82\n\xe2\x94\x82 \xe2\x94\x82     type_ = None                                                                             \xe2\x94\x82 \xe2\x94\x82\n\xe2\x94\x82 \xe2\x94\x82     value = None                                                                             \xe2\x94\x82 \xe2\x94\x82\n\xe2\x94\x82 \xe2\x95\xb0\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x95\xaf\n
Run Code Online (Sandbox Code Playgroud)\n

无论如何,我觉得在让表列通过调用自动执行 SQL 命令的过程中我已经搞错了func。对这个有什么想法吗?我在 SQLAlchemy 2.0 文档中没有找到任何直接的示例,除了对类似问题的非常棒的评论之外,我还没有找到任何可行的解决方案。

\n

感谢您考虑!

\n

我实现了一个 SQLAlchemy 2.0 映射列,其 server_default 为 func.now(),期望该列在 INSERT 操作期间自动填充。在插入操作期间,SQLAlchemy 抛出异常,声称违反了列 NOT NULLABLE 约束 - 因此它不会自动填充。

\n

Rem*_*emi 7

发布我自己的问题的答案,以说明实际的工作原理(实际问题仍然存在,但简化的变体确实按照我期望的方式工作。)

import datetime

from sqlalchemy import Integer, String, DateTime
from sqlalchemy import create_engine
from sqlalchemy.sql import func
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import Session


class Base(DeclarativeBase):
    pass


class MyTable(Base):
    __tablename__ = "my_table"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String)
    created_date: Mapped[datetime.datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )


def initialize_engine(filename):
    return create_engine(f"sqlite+pysqlite:///{filename}", echo=True)


def initialize_tables(engine):
    Base.metadata.create_all(engine)


def add_row(engine, name):
    this_row = MyTable(name=name)
    print(this_row)
    with Session(engine) as session:
        session.add(this_row)
        session.commit()


my_file = "test.db"

my_engine = initialize_engine(my_file)
initialize_tables(my_engine)

add_row(my_engine, "Dave")
Run Code Online (Sandbox Code Playgroud)

这会产生结果:

python datetest.py
2023-02-06 11:02:41,157 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2023-02-06 11:02:41,158 INFO sqlalchemy.engine.Engine PRAGMA main.table_info("my_table")
2023-02-06 11:02:41,158 INFO sqlalchemy.engine.Engine [raw sql] ()
2023-02-06 11:02:41,158 INFO sqlalchemy.engine.Engine COMMIT
<__main__.MyTable object at 0x000002CC767ECD50>
2023-02-06 11:02:41,159 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2023-02-06 11:02:41,160 INFO sqlalchemy.engine.Engine INSERT INTO my_table (name) VALUES (?) RETURNING id, created_date
2023-02-06 11:02:41,160 INFO sqlalchemy.engine.Engine [generated in 0.00020s] ('Dave',)
2023-02-06 11:02:41,171 INFO sqlalchemy.engine.Engine COMMIT
Run Code Online (Sandbox Code Playgroud)

正确工作的数据库中的架构如下:

sqlite> .schema my_table
CREATE TABLE my_table (
        id INTEGER NOT NULL,
        name VARCHAR NOT NULL,
        created_date DATETIME DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
        PRIMARY KEY (id)
);
Run Code Online (Sandbox Code Playgroud)

所以...我所要做的就是弄清楚为什么我的原始代码没有进行简单的变化!