在 SQLModel 中使用表名动态设置 sql-default 值

Mat*_*ger 5 python sqlalchemy alembic sqlmodel

我正在尝试在 SQLModel 中创建一个基类,如下所示:

class BaseModel(SQLModel):
    @declared_attr
    def __tablename__(cls) -> str:
        return cls.__name__

    guid: Optional[UUID] = Field(default=None, primary_key=True)

class SequencedBaseModel(BaseModel):
    sequence_id: str = Field(sa_column=Column(VARCHAR(50), server_default=text(f"SELECT '{TABLENAME}_' + convert(varchar(10), NEXT VALUE FOR dbo.sequence)")))
Run Code Online (Sandbox Code Playgroud)

所以我得到了一个这样的表:

class Project(SequencedBaseModel):
    ...
Run Code Online (Sandbox Code Playgroud)

Project其中 alembic 将为包含列guid和的表生成迁移sequence_id。序列 ID 的默认值是使用以下命令生成的序列

SELECT '{TABLENAME}_' + convert(varchar(10), NEXT VALUE FOR dbo.sequence)
Run Code Online (Sandbox Code Playgroud)

并且应该将值Project_1, ,Project_2插入到项目表中...

关于如何动态设置表名有什么想法吗?我无法使用构造函数来设置列,因为 alembic 忽略它们,我无法访问该__tablename__()函数,或者cls,因为列是静态的...

Bor*_*jaX 3

不幸的是,如果您有一个依赖于 a 的属性@declared_attr,它也必须是 a @declared_attr,因为 SqlAlchemy 将等到整个映射完成并且类获得要解析的实际表(至少,这是我的理解)。现在:declared_attr(s) 是一个 SqlAlchemy 概念,而 的想法Field是一个 SQLModel 概念,并且它们似乎不会就这种“”“deferred””属性事物相互交谈(“”“deferred”” “从某种意义上说,在映射完成并且表已知之前不会对其进行评估,而不是像等待访问一样推迟)......至少,据我所知。

\n

可以(也许?希望如此?)执行类似于此 SQLModel GitHub 问题中建议的操作:“推迟” SqlAlchemy 列并为 SQLModel 字段指定别名:

\n
class SequencedBaseModel(BaseModel):\n    sequence_id: str = Field(alias="sequence_id")\n\n    @declared_attr\n    def sequence_id(cls):\n        return Column(\n            \'sequence_id\',\n            VARCHAR(50),\n            server_default=text(f"SELECT \'{cls.__tablename__}_\'"\n                                f" + convert(varchar(10), NEXT VALUE FOR dbo.sequence)"))\n\n\nclass Project(SequencedBaseModel, table=True):\n    pass\n
Run Code Online (Sandbox Code Playgroud)\n

运行将生成一个迁移文件,并在\'s中扩展了alembic revision --autogenerate -m "init"正确的__tablename__ + \'_\'(意思是:) :Product_server_defaultSELECT...

\n
def upgrade() -> None:\n    op.create_table(\'Project\',\n    sa.Column(\'guid\', sqlmodel.sql.sqltypes.GUID(), nullable=False),\n    sa.Column(\'sequence_id\', sa.VARCHAR(length=50), server_default=sa.text("SELECT \'Project_\' + convert(varchar(10), NEXT VALUE FOR dbo.sequence)"), nullable=True),\n    sa.PrimaryKeyConstraint(\'guid\'),\n    # ... \n    # ... \n
Run Code Online (Sandbox Code Playgroud)\n

这假设您的 alembic 环境已正确配置。我忍不住指出 alembic 将使用属性sqlmodel.sql.sqltypes.GUID()的列类型生成迁移guid,因此您需要确保sqlmodel在每个迁移文件上导入该包。可能通过按照此链接中script.py.mako所述编辑模板,其中显示您必须添加.import sqlmodel # NEW

\n

\xe2\x9a\xa0\xef\xb8\x8f 我试图测试这个,但我不完全知道来自哪里dbo.sequence(也许是 SQL 服务器?)。我使用PostgreSQL 序列(我将其命名为so75719072)来模拟它。这意味着我无法确认 的语法DEFAULT SELECT...在您的情况下是否有效。我非常怀疑您是否能够使用 a 的结果SELECT作为列的默认值,但希望我错了。

\n
import uuid\nfrom typing import Optional\nfrom uuid import UUID\n\nfrom sqlalchemy import Column, VARCHAR, text\nfrom sqlalchemy.orm import declared_attr\nfrom sqlmodel import SQLModel, Field, create_engine, Session, select\n\n\nclass BaseModel(SQLModel):\n    __table_args__ = {\'schema\': \'SO-75719072\'}\n\n    @declared_attr\n    def __tablename__(cls) -> str:\n        return cls.__name__\n\n    guid: Optional[UUID] = Field(default=None, primary_key=True)\n\n\nclass SequencedBaseModel(BaseModel):\n    sequence_id: str = Field(alias="sequence_id")\n\n    @declared_attr\n    def sequence_id(cls):\n        return Column(\n            \'sequence_id\',\n            VARCHAR(50),\n            server_default=text(f"nextval(\'so75719072\')"))\n\n\nclass Project(SequencedBaseModel, table=True):\n    pass\n\n\nif __name__ == "__main__":\n    engine = create_engine(\n        "postgresql+psycopg2://postgres:postgrespw@localhost:32768/stackoverflow")\n\n    with Session(engine) as session:\n        for i in range(3):\n            proj1 = Project(guid=uuid.uuid4())\n            session.add(proj1)\n            session.commit()\n\n    with Session(engine) as session:\n        statement = select(Project).where(Project.sequence_id.in_(["1", "2", "3"]))\n        for project in session.exec(statement):\n            print(f"guid: {project.guid}")\n
Run Code Online (Sandbox Code Playgroud)\n

产生以下输出:

\n
guid: c5e5902d-e224-48f1-95f5-fa47a73f7b05\nguid: 1c25550b-258c-49c5-9acc-90ae7ad8460c\nguid: eb84e90c-9449-4974-8eb4-bad98728b0f9\n
Run Code Online (Sandbox Code Playgroud)\n

它来自 Postgres 中的下表:

\n
# select * from "SO-75719072"."Project";\n                 guid                 | sequence_id\n--------------------------------------+-------------\n c5e5902d-e224-48f1-95f5-fa47a73f7b05 | 1\n 1c25550b-258c-49c5-9acc-90ae7ad8460c | 2\n eb84e90c-9449-4974-8eb4-bad98728b0f9 | 3\n(3 rows)\n
Run Code Online (Sandbox Code Playgroud)\n