带有Sqlalchemy的Postgres中的枚举阵列



我一直在使用Postgres和Sqlalchemy的一系列枚举,在过去的一年中成功使用:

class MyModel(BaseModel):
    enum_field = Column(postgresql.ARRAY(EnumField(MyEnum, native_enum=False)))

EnumField来自sqlalchemy_enum34库,这是一个围绕内置枚举的小包装,将python枚举用作python表示而不是字符串。

尽管文档说,但不支持枚举的数组,但我猜它有效,因为我选择了'native_enum = false'。最近,我注意到它不再起作用,我认为这是由于SQLA 1.0升级到1.1,但我不确定。

问题是,它会生成无效的DQL:

CREATE TABLE my_model (
    enum_field VARCHAR(5)[3] NOT NULL CHECK (contexts IN ('ONE', 'TWO', 'THREE'))
)

我遇到的错误是:

ERROR:  malformed array literal: "ONE"
DETAIL:  Array value must start with "{" or dimension information.

知道我怎么能恢复我的枚举数组?
顺便说一句:当它工作时,实际上没有创建检查约束,只是一系列变化。只要我可以在Python代码中使用枚举(例如query.filter(enum_field==MyEnum.ONE)

,我就可以接受。

我在sqlalchemy源代码中找到了不错的解决方法:

import re
from sqlalchemy import TypeDecorator, cast
from sqlalchemy.dialects.postgresql import ARRAY

class ArrayOfEnum(TypeDecorator):
    impl = ARRAY
    def bind_expression(self, bindvalue):
        return cast(bindvalue, self)
    def result_processor(self, dialect, coltype):
        super_rp = super(ArrayOfEnum, self).result_processor(dialect, coltype)
        def handle_raw_string(value):
            inner = re.match(r"^{(.*)}$", value).group(1)
            return inner.split(",") if inner else []
        def process(value):
            if value is None:
                return None
            return super_rp(handle_raw_string(value))
        return process

现在:

achievements = Column(ArrayOfEnum(Enum(AchievementsType)))

,然后:

career.achievements = [AchievementsType.world, AchievementsType.local]

在现代sqlalchemy中,您不必为此定义自定义类型:

import sqlalchemy.dialects.postgresql as pg
class MyModel(Base):
    ...
    flags = Column(pg.ARRAY(sa.Enum(MyEnum, 
                   create_constraint=False, native_enum=False)))

当我需要一系列枚举时,我在这里使用了Mike Bayer的食谱:https://bitbucket.org/zzzeek/zzzeek/sqlalchemy/ssqlalchemy/sissues/3467/array-oray-of-enaray-of-enums-does-does-does-does-does-does-does-does-does-does-does-does-does-does-does-does-doe-Not-Allow-Assigning#comment-19370832

编辑:问题移至https://github.com/sqlalchemy/sqlalchemy/sissues/3467

这就是创建这样的自定义类型:

import sqlalchemy as sa
class ArrayOfEnum(ARRAY):
    def bind_expression(self, bindvalue):
        return sa.cast(bindvalue, self)
    def result_processor(self, dialect, coltype):
        super_rp = super(ArrayOfEnum, self).result_processor(dialect, coltype)
        def handle_raw_string(value):
            inner = re.match(r"^{(.*)}$", value).group(1)
            return inner.split(",")
        def process(value):
            return super_rp(handle_raw_string(value))
        return process

我已经有一段时间没有使用过,所以我不确定它继续工作。

它与您的enum34库不同,所以也许不会有相同的问题?

Mike Bayer在SQLalchemy邮件列表上回答:

您可能要添加create_constraint = false,查看是否有效

http://docs.sqlalchemy.org/en/latest/core/type_basics.html?highlight = enum#sqlalchemy.types.enum.params.createe_contraint

我现在可以创建表(无需检查)。

如果在这里找到自己的路,将sqlalchemy更新为> = 1.3.17应该整理出来。

请参见发行说明,网址:https://docs.sqlalchemy.org/en/13/changelog/changelog/changelog_13.html#change-e57f5913ab5913ab592a92a9c044444444747636eg

最新更新