简体   繁体   English

带有 SQLAlchemy 的 Postgres 中的枚举数组

[英]Array of Enum in Postgres with SQLAlchemy

I've been using an array of enums with postgres and SQLAlchemy successfully over the past year like so:在过去的一年里,我一直在使用一系列带有 postgres 和 SQLAlchemy 的枚举,如下所示:

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

The EnumField is from the sqlalchemy_enum34 library, a small wrapper around the builtin enum that uses Python enums as Python representation instead of strings.EnumField是从sqlalchemy_enum34库,周围使用Python枚举像Python表示,而不是字符串的内置枚举小包装。

Although the docs say , array of enum is not supported, I guess it worked, because I chose 'native_enum=False'.尽管文档说不支持枚举数组,但我想它有效,因为我选择了“native_enum=False”。 Recently I noticed that it doesn't work anymore, I think it's due to the upgrade from SQLA 1.0 to 1.1, but I'm not sure.最近我发现它不再工作了,我认为这是由于从 SQLA 1.0 升级到 1.1,但我不确定。

The problem is, that it generates invalid DQL:问题是,它生成了无效的 DQL:

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

The error I get is:我得到的错误是:

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

Any idea how I can get back my enum array?知道如何取回枚举数组吗?
By the way: when it worked, no CHECK constraint was actually created, just an array of varying.顺便说一句:当它工作时,实际上没有创建 CHECK 约束,只是一个变化的数组。 I'm ok with that as long as I can use enums in my Python code (eg query.filter(enum_field==MyEnum.ONE) )只要我可以在我的 Python 代码中使用枚举,我就可以接受(例如query.filter(enum_field==MyEnum.ONE)

I found nice workaround in SqlAlchemy source code:我在 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

And now:现在:

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

And then:进而:

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

When I needed an array of enums I used the recipe from Mike Bayer here: https://bitbucket.org/zzzeek/sqlalchemy/issues/3467/array-of-enums-does-not-allow-assigning#comment-19370832当我需要一系列枚举时,我使用了 Mike Bayer 的食谱: https : //bitbucket.org/zzzeek/sqlalchemy/issues/3467/array-of-enums-does-not-allow-assigning#comment-19370832

EDIT: Issue moved to https://github.com/sqlalchemy/sqlalchemy/issues/3467编辑:问题移至https://github.com/sqlalchemy/sqlalchemy/issues/3467

This is, create a custom type like this:也就是说,创建一个像这样的自定义类型:

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

I haven't used this for a while so I'm not certain that it continues to work.我已经有一段时间没有使用它了,所以我不确定它是否会继续工作。

It's not the same code as your enum34 library so maybe it won't have the same problems?它与您的 enum34 库的代码不同,所以也许它不会有同样的问题?

In modern SqlAlchemy, you don't have to define a custom type for this:在现代 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 answered on the sqlalchemy mailing list : Mike Bayersqlalchemy 邮件列表上回答:

you probably want to add create_constraint=False, see if that works您可能想添加 create_constraint=False,看看是否有效

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

I can now create the table (without any CHECK).我现在可以创建表(没有任何检查)。

If you find your way here, updating SQLAlchemy to >=1.3.17 should sort you out.如果您在这里找到了自己的方式,将 SQLAlchemy 更新为 >=1.3.17 应该可以解决您的问题。

See the release notes at: https://docs.sqlalchemy.org/en/13/changelog/changelog_13.html#change-e57f5913ab592a9c044cad747636edd8请参阅发行说明: https : //docs.sqlalchemy.org/en/13/changelog/changelog_13.html#change-e57f5913ab592a9c044cad747636edd8

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM