(注:我将在下面回答我自己的问题。但是,评论, 欢迎其他见解或替代方法!我收到了 F. Caselli 在 SQLAlchemy 论坛上提供的宝贵帮助,其中 帮助我缩小了寻找解决方案的范围。)
您好,我正在尝试在 SqlAlchemy 2.0 中使用 mapped_column()
直接在 ORM 定义
中创建一个COMPUTED 列,但我没有成功(我检查了 Google 并尝试找到答案在文档中)。
我知道对于核心模型,通过以下方式声明一个表就足够了(玩具示例,其中
"area"
是一个计算列):
square = Table(
"square",
metadata_obj,
Column("id", Integer, primary_key=True),
Column("side", Integer),
Column("area", Integer, Computed("side * side")),
)
这相当于以下 SQL DDL 语句 (Postgresql >12):
CREATE TABLE square (
id SERIAL NOT NULL,
side INTEGER,
area INTEGER GENERATED ALWAYS AS (side * side) STORED,
PRIMARY KEY (id)
)
但是,当尝试使用 Mapped[]
和
mapped_column()
在 ORM中执行等效操作时,我似乎找不到解决方案。
我想象下面的东西可能会起作用,但事实并非如此:
class Square(Base):
...
area: Mapped[int] = mapped_column(Computed("side*side"))
在此之前,我还尝试过其他配置,例如将
Computed()
传递为 server_default=
,以及使用 FetchedValue
,但在所有情况下,我都遇到了错误。
我也来到了
@hybrid_property
,但我知道它不一样(因为我需要计算列成为表的DDL定义的一部分,并且@hybrid_property
似乎让计算在选择期间完成,而不是作为DDL ).
如果有人能指导我正确使用
mapped_column()
和 Computed()
!,我将不胜感激
发现缺失的部分只是参数
area: Mapped[int] = mapped_column(Computed("side*side"))
!后,我们能够缩小搜索范围,使
init=False
发挥作用
原来我的
Base
类是从MappedAsDataclass
派生而来的,而数据类的__init__()
方法的创建并没有按照此类Base
类的要求正确处理。
显然,
MappedAsDataclass
机制不会自动从Computed()
方法中排除__init__()
列,因此我们必须在init=False
定义中显式设置mapped_column()
。
总而言之,我的案例的解决方案是声明
area
列,如下所示:
class Base(MappedAsDataclass, DeclarativeBase):
pass
class Square(Base):
...
# if computed column use init=False if Base is a MappedAsDataclass
area: Mapped[int] = mapped_column(Computed("side*side"), init=False)
...
感谢 F. Caselli 帮助缩小了解决方案的搜索范围 sqlalchemy 论坛!!