使用SQLAlchemy的Postgres中的枚举数组

时间:2016-12-21 08:34:28

标签: python arrays postgresql enums sqlalchemy

过去一年,我一直在使用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)

,我就可以了

5 个答案:

答案 0 :(得分:1)

当我需要一系列枚举时,我在这里使用了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库的代码不同,所以它可能会遇到同样的问题吗?

答案 1 :(得分:1)

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

import sqlalchemy.dialects.postgresql as pg

class MyModel(Base):
    ...
    flags = Column(pg.ARRAY(sa.Enum(MyEnum, 
                   create_constraint=False, native_enum=False)))

答案 2 :(得分:0)

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

我现在可以创建表格(没有任何CHECK)。

答案 3 :(得分:0)

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

答案 4 :(得分:0)

如果在这里找到自己的出路,请将SQLAlchemy更新为> = 1.3.17应该可以解决问题。

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