存储ENUM值的PostgreSQL ARRAY

时间:2015-06-11 14:39:03

标签: python postgresql sqlalchemy

我有一张可以拥有状态的表:

statuses = ['unmoderated', 'nominee', 'finalist', 'winner']
status = db.Enum(
    *statuses, name='enum_nomination_status', metadata=db.metadata)


class Nomination(db.Model):
    status = db.Column(status, default='unmoderated')

我现在想要一个包含多个状态的列的表:

class Judge(db.Model):
    statuses = db.Column(ARRAY(status, dimensions=1))

然而,上述方法导致我出现此错误:

ProgrammingError: (psycopg2.ProgrammingError) column "statuses" is of type enum_nomination_status[] but expression is of type text[]
LINE 1: ...4, 'Name', ARRAY['unm...
                  ^
HINT:  You will need to rewrite or cast the expression.

所以我尝试创建一个自定义类型,使其转换为枚举类型:

class STATUS_ARRAY(TypeDecorator):
    impl = ARRAY(status, dimensions=1)

    def process_bind_param(self, value, dialect):
        if value is None:
            return value
        else:
            return cast(array(value), ARRAY(status, dimensions=1))

但这会导致段错误。

我也尝试过投放各个项目:

class STATUS_ARRAY(TypeDecorator):
    impl = ARRAY(status, dimensions=1)

    def process_bind_param(self, value, dialect):
        if value is None:
            return value
        else:
            return array(cast(s, status) for s in value)

但我明白了:

ProgrammingError: (psycopg2.ProgrammingError) can't adapt type 'Cast' [SQL: 'INSERT INTO judge (statuses) VALUES (%(statuses)s)'] [parameters: {'statuses': [<sqlalchemy.sql.elements.Cast object at 0x7fc8bb69c710>]}]

我承认,我大多尝试不同的铸造组合,而不知道幕后发生了什么。我尝试查看底层的ENUM实现,看看我是否可以在没有强制转换的情况下获得某种本地枚举类型但我无法看到任何内容。我抓着稻草。

感谢您的帮助:)

1 个答案:

答案 0 :(得分:2)

我查看了Wichert Akkerman发布的Issue 3467,并发布了此解决方法。感谢Mike Bayer。在代码中声明以下类(当然还有必要的导入):

     var num = 153391689;  
     function set(val, loc) {
         num |= val << (loc * 3);
     }
     function get(loc) {
         return (num & 7 << loc * 3) / Math.pow(8, loc);
     }

from sqlalchemy.dialects.postgresql import ARRAY from sqlalchemy import cast class ArrayOfEnum(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): if value==None: return [] inner = re.match(r"^{(.*)}$", value).group(1) return inner.split(",") def process(value): return super_rp(handle_raw_string(value)) return process 现在是一种特殊的列类型,可以在模型定义中使用。

所以而不是

ArrayOfEnum

现在你可以做到:

class Judge(db.Model):
    statuses = db.Column(ARRAY(status))

现在,在您的代码中,您可以使用列表为class Judge(db.Model): statuses = db.Column(ArrayOfEnum(status)) 分配值,并在保存时执行正确的转换:

statuses