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

问题描述 投票:0回答:1

我正在尝试在 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)")))

所以我得到了这样一张桌子:

class Project(SequencedBaseModel):
    ...

其中 alembic 将为表

Project
生成迁移,其中包含列
guid
sequence_id
。 sequence-id 的默认值是用

生成的序列
SELECT '{TABLENAME}_' + convert(varchar(10), NEXT VALUE FOR dbo.sequence)

并且应该在项目表中插入值

Project_1
Project_2
...

关于如何动态设置表名的任何想法?我不能使用构造函数来设置列,因为 alembic 忽略了它们,我无法访问

__tablename__()
函数或
cls
,因为列是静态的...

python sqlalchemy alembic sqlmodel
1个回答
1
投票

不幸的是,如果你有一个依赖于

@declared_attr
的属性,它也必须是一个
@declared_attr
,因为它们会等到整个映射完成才能解析(至少,这是我的理解)。现在:
declared_attr
(s) 是一个 SqlAlchemy 概念,而
Field
的想法是一个 SQLModel 概念,他们似乎并没有就这个 """"deferred""" 属性(at至少,据我所知)。

可以(也许?希望?)做一些像这个SQLModel GitHub问题中推荐的事情:延迟SqlAlchemy列并为SQLModel字段起别名:

class SequencedBaseModel(BaseModel):
    sequence_id: str = Field(alias="sequence_id")

    @declared_attr
    def sequence_id(cls):
        return Column(
            'sequence_id',
            VARCHAR(50),
            server_default=text(f"SELECT '{cls.__tablename__}_'"
                                f" + convert(varchar(10), NEXT VALUE FOR dbo.sequence)"))


class Project(SequencedBaseModel, table=True):
    pass

运行

alembic revision --autogenerate -m "init"
将生成一个迁移文件,其中适当的
__tablename__ + '_'
(意思是:
Product_
)在
server_default
SELECT...
:

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

这假设您的 alembic 环境已正确配置。我忍不住指出 alembic 将使用

sqlmodel.sql.sqltypes.GUID()
作为
guid
属性的列类型生成迁移,因此您需要确保在每次迁移时导入包
sqlmodel
。可能通过编辑模板
script.py.mako
,如在此链接中所述,它表明您必须添加
import sqlmodel             # NEW
.

⚠️ 我试图对此进行测试,但我不完全知道

dbo.sequence
来自哪里(也许是 SQL 服务器?)。我使用了一个 PostgreSQL 序列(我将其命名为
so75719072
)来模拟它。这意味着我无法确认
DEFAULT SELECT...
的语法在您的情况下是否有效。我很怀疑你是否能够使用
SELECT
的结果作为列的默认值,但希望我错了。

import uuid
from typing import Optional
from uuid import UUID

from sqlalchemy import Column, VARCHAR, text
from sqlalchemy.orm import declared_attr
from sqlmodel import SQLModel, Field, create_engine, Session, select


class BaseModel(SQLModel):
    __table_args__ = {'schema': 'SO-75719072'}

    @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(alias="sequence_id")

    @declared_attr
    def sequence_id(cls):
        return Column(
            'sequence_id',
            VARCHAR(50),
            server_default=text(f"nextval('so75719072')"))


class Project(SequencedBaseModel, table=True):
    pass


if __name__ == "__main__":
    engine = create_engine(
        "postgresql+psycopg2://postgres:postgrespw@localhost:32768/stackoverflow")

    with Session(engine) as session:
        for i in range(3):
            proj1 = Project(guid=uuid.uuid4())
            session.add(proj1)
            session.commit()

    with Session(engine) as session:
        statement = select(Project).where(Project.sequence_id.in_(["1", "2", "3"]))
        for project in session.exec(statement):
            print(f"guid: {project.guid}")

产生以下输出:

guid: c5e5902d-e224-48f1-95f5-fa47a73f7b05
guid: 1c25550b-258c-49c5-9acc-90ae7ad8460c
guid: eb84e90c-9449-4974-8eb4-bad98728b0f9

来自 Postgres 中的下表:

# select * from "SO-75719072"."Project";
                 guid                 | sequence_id
--------------------------------------+-------------
 c5e5902d-e224-48f1-95f5-fa47a73f7b05 | 1
 1c25550b-258c-49c5-9acc-90ae7ad8460c | 2
 eb84e90c-9449-4974-8eb4-bad98728b0f9 | 3
(3 rows)
© www.soinside.com 2019 - 2024. All rights reserved.