簡體   English   中英

帶有 SQLAlchemy 的 Postgres 中的枚舉數組

[英]Array of Enum in Postgres with SQLAlchemy

在過去的一年里,我一直在使用一系列帶有 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.

知道如何取回枚舉數組嗎?
順便說一句:當它工作時,實際上沒有創建 CHECK 約束,只是一個變化的數組。 只要我可以在我的 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]

當我需要一系列枚舉時,我使用了 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 庫的代碼不同,所以也許它不會有同樣的問題?

在現代 SqlAlchemy 中,您不必為此定義自定義類型:

import sqlalchemy.dialects.postgresql as pg

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

Mike Bayersqlalchemy 郵件列表上回答:

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

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

我現在可以創建表(沒有任何檢查)。

如果您在這里找到了自己的方式,將 SQLAlchemy 更新為 >=1.3.17 應該可以解決您的問題。

請參閱發行說明: 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