【问题标题】:Array of Enum in Postgres with SQLAlchemy使用 SQLAlchemy 在 Postgres 中的枚举数组
【发布时间】:2017-05-06 14:48:45
【问题描述】:

在过去的一年里,我一直在使用一系列带有 postgres 和 SQLAlchemy 的枚举,如下所示:

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

EnumField 来自 sqlalchemy_enum34 库,它是内置枚举的一个小型包装器,它使用 Python 枚举作为 Python 表示而不是字符串。

虽然docs say, 枚举数组不受支持,但我想它有效,因为我选择了'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.

知道如何取回我的枚举数组吗?
顺便说一句:当它起作用时,实际上并没有创建 CHECK 约束,只是一个可变数组。只要我可以在 Python 代码中使用枚举(例如query.filter(enum_field==MyEnum.ONE)),我就可以接受。

【问题讨论】:

    标签: python arrays postgresql enums sqlalchemy


    【解决方案1】:

    我在 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]
    

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        【解决方案3】:

        当我需要一组枚举时,我在这里使用了 Mike Bayer 的配方:https://bitbucket.org/zzzeek/sqlalchemy/issues/3467/array-of-enums-does-not-allow-assigning#comment-19370832

        编辑:问题移至https://github.com/sqlalchemy/sqlalchemy/issues/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 库的代码不同,所以它可能不会有同样的问题?

        【讨论】:

        • 我之前尝试过,但是,它不会改变在 Postgres 中创建数组的方式,生成的 DDL 是相同的。如您所见,它只处理与默认 ARRAY 不同的结果。
        • 第四行的“sa”是什么?
        • 嘿@Andru,我在我的代码库中找不到这段代码了,所以我不能肯定地告诉你(不深入研究 git 历史)。我怀疑这只是 SQLAlchemy 的别名导入。我已更新答案以填写此内容。
        【解决方案4】:

        Mike Bayersqlalchemy mailing list 上回答:

        您可能想要添加 create_constraint=False,看看是否有效

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

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

        【讨论】:

          【解决方案5】:

          如果您在这里找到了自己的方式,将 SQLAlchemy 更新到 >=1.3.17 应该可以解决您的问题。

          查看发布说明:https://docs.sqlalchemy.org/en/13/changelog/changelog_13.html#change-e57f5913ab592a9c044cad747636edd8

          【讨论】:

            猜你喜欢
            • 2020-07-28
            • 1970-01-01
            • 1970-01-01
            • 2023-04-04
            • 2018-03-12
            • 1970-01-01
            • 1970-01-01
            • 2017-12-11
            • 2018-01-17
            相关资源
            最近更新 更多