我尝试在SQLAlchemy 2.0中使用Enums
和mapped_column
。到目前为止,我有以下代码(取自另一个问题):
from sqlalchemy.dialects.postgresql import ENUM as pgEnum
import enum
class CampaignStatus(str, enum.Enum):
activated = "activated"
deactivated = "deactivated"
CampaignStatusType: pgEnum = pgEnum(
CampaignStatus,
name="campaignstatus",
create_constraint=True,
metadata=Base.metadata,
validate_strings=True,
)
class Campaign(Base):
__tablename__ = "campaign"
id: Mapped[UUID] = mapped_column(primary_key=True, default=uuid4)
created_at: Mapped[dt.datetime] = mapped_column(default=dt.datetime.now)
status: Mapped[CampaignStatusType] = mapped_column(nullable=False)
但是,在构造Campaign
类本身时会出现以下错误。
Traceback (most recent call last):
File "<stdin>", line 27, in <module>
class Campaign(Base):
...
AttributeError: 'ENUM' object has no attribute '__mro__'
有没有什么方法能让它奏效?
来自ENUM type in SQLAlchemy with PostgreSQL的响应不适用,因为我使用的是SQLAlchemy版本2,并且这些答案没有使用mapped_column
或Mapped
类型。此外,从CampaignStatus
中删除str
也没有帮助。
1条答案
按热度按时间pengsaosao1#
导致
AttributeError
的__mro__
相关问题的关键在于CampaignStatusType
不是一个类,而是一个sqlalchemy.dialects.postgresql.ENUM
类型的示例变量(使用pyright
可以验证这一点-因为它抱怨Mapped[CampaignStatusType]
是一个“非法类型注解:除非是类型别名,否则不允许使用变量“)。作为一个测试,用Mapped[CampaignStatus]
替换status
的类型注解确实解决了这个问题(pyright
没有报告任何错误),但这并没有将列类型与所需的postgresql方言挂钩。因此,在使用方言特定的枚举类型时,解决这个问题的唯一方法是使用无注解的构造:
然而,如果仍然需要类型注解,即无论
Mapped
是什么,都必须是一个类型,并且sqlalchemy.dialects.postgresql.ENUM
(作为pgEnum
导入)是示例CampaignStatusType
的基础类型,可以认为以下可能是一个解决方案虽然它工作,但它实际上并不反映数据将表示什么,所以不要实际上这样做。此外,它之所以有效,是因为在传递特定列类型时会忽略类型注解,因此在其中放置任何内容都可以在具有无效类型的情况下工作。
现在,考虑到SQLAlchemy现在是2.0(因为问题明确需要这个较新的版本),也许现在应该检查文档并查看本地枚举。
修改文档中的示例,现在可以导出以下MVCE,使用传递给PostgreSQL方言特定ENUM类型的所有预期关键字参数,而不是传递给通用
sqlalchemy.Enum
(除了metadata=Base.metadata
,因为这完全是多余的):现在加入用法:
上面的代码将生成
failed to insert with 'some_unvalidated_string'
作为输出,表明未验证的字符串将不会被插入,而Map到某个枚举的已验证字符串将被插入而不会出现问题。此外,pyright
不会产生错误(尽管老实说,这不一定是一个好的指标,因为Python中的类型提示仍然相当不成熟,因为pyright
在一开始就没有检测到错误的示例,无论Mapped
内部发生了什么,但我离题了)。使用
psql
查看新创建的实体当然,在不删除
campaign
表的情况下,不能删除枚举:因此,尽管只使用通用的SQLAlchemy类型,但枚举或多或少地表现为预期的行为,而不需要特定于方言的导入。