【问题标题】:SQLAlchemy update record with case clause and enumSQLAlchemy 使用 case 子句和枚举更新记录
【发布时间】:2017-07-21 12:39:11
【问题描述】:

我已经定义了以下模型和enum

class StatusEnum(enum.Enum):
    NEW = 'NEW'
    PROCESSED = 'PROCESSED'
    IN_PROGRESS = 'IN_PROGRESS'

class RequestLog(Base):
    __tablename__ = 'request_log'

    ...
    status = Column(Enum(StatusEnum))
    ...

我正在尝试通过以下方式更新记录:

>>> session.query(RequestLog).filter(RequestLog.id.in_([8])).update(
{'status': case(
    [(RequestLog.attempt_done_count == RequestLog.attempt_count - 1, StatusEnum.PROCESSED)],
    else_=StatusEnum.IN_PROGRESS)},
synchronize_session=False)

在此期间,我收到一个错误:

sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) can't adapt type 'StatusEnum' [SQL: 'UPDATE request_log SET status=CASE WHEN (request_log.attempt_done_count = request_log.attempt_count - %(attempt_count_1)s) THEN %(param_1)s ELSE %(param_2)s END WHERE request_log.id IN (%(id_1)s)'] [parameters: {'param_2': <StatusEnum.IN_PROGRESS: 'IN_PROGRESS'>, 'attempt_count_1': 1, 'param_1': <StatusEnum.PROCESSED: 'PROCESSED'>, 'id_1': 8}]

【问题讨论】:

  • 如果直接作为值传递,查询编译器似乎能够转换枚举对象,但如果它们是 SQL 表达式的一部分,则不能。找不到任何关于这是否是设计的参考,也找不到关于该主题的any issues。考虑打开一个?
  • 检查您的 SQLA 版本
  • &gt;&gt;&gt; sqlalchemy.__version__'1.1.11'

标签: python enums sqlalchemy python-3.4


【解决方案1】:

对于一个简单的 Python 枚举对象绑定值,运行 bind processor of sqltypes.Enum,其中 uses the string name of the enum object 存在问题:

In [27]: session.query(RequestLog).filter(RequestLog.id.in_([8])).update(
    ...:     {'status': StatusEnum.NEW},
    ...:     synchronize_session=False)
2017-07-24 15:15:43,848 INFO sqlalchemy.engine.base.Engine UPDATE request_log SET status=%(status)s WHERE request_log.id IN (%(id_1)s)
INFO:sqlalchemy.engine.base.Engine:UPDATE request_log SET status=%(status)s WHERE request_log.id IN (%(id_1)s)
2017-07-24 15:15:43,848 INFO sqlalchemy.engine.base.Engine {'status': 'NEW', 'id_1': 8}
INFO:sqlalchemy.engine.base.Engine:{'status': 'NEW', 'id_1': 8}
Out[27]: 0

这似乎不会递归地发生在 SQL 表达式中,所以在你的case() 中,枚举对象绑定值被传递给不知道如何处理它们的 psycopg。要模拟 SQL 表达式中的行为,您可以手动传递 names of the enum objects,并进行适当的转换:

In [60]: session.query(RequestLog).filter(RequestLog.id.in_([8])).update(
    ...:     {'status': case(
    ...:         [(true(), StatusEnum.PROCESSED.name)],
    ...:         else_=StatusEnum.IN_PROGRESS.name).cast(RequestLog.status.type)},
    ...:     synchronize_session=False)
2017-07-24 15:40:52,853 INFO sqlalchemy.engine.base.Engine UPDATE request_log SET status=CAST(CASE WHEN true THEN %(param_1)s ELSE %(param_2)s END AS statusenum) WHERE request_log.id IN (%(id_1)s)
INFO:sqlalchemy.engine.base.Engine:UPDATE request_log SET status=CAST(CASE WHEN true THEN %(param_1)s ELSE %(param_2)s END AS statusenum) WHERE request_log.id IN (%(id_1)s)
2017-07-24 15:40:52,853 INFO sqlalchemy.engine.base.Engine {'param_2': 'IN_PROGRESS', 'param_1': 'PROCESSED', 'id_1': 8}
INFO:sqlalchemy.engine.base.Engine:{'param_2': 'IN_PROGRESS', 'param_1': 'PROCESSED', 'id_1': 8}
Out[60]: 0

这很难看,我有点确定存在更好的方法,但目前这是我能想到的最好的方法。

【讨论】:

  • 抱歉,这不起作用。我实际上尝试过casttype_coerceliteral 和其他选项,但没有任何成功。我的时间有限,所以我只是循环迭代它。我知道这是不好的方法,但它有效。
  • 任何特定的错误/原因对您不起作用?在测试我的本地 Postgres 时,它运行良好。
  • session.query(RequestLog).filter(RequestLog.id.in_([8])).update({'status': case([(RequestLog.attempt_done_count == RequestLog.attempt_count - 1, StatusEnum.PROCESSED)], else_=StatusEnum.IN_PROGRESS).cast(RequestLog.status.type)}, synchronize_session=False 我得到了同样的can't adapt type 'StatusEnum'
  • psql (9.4.9) psycopg2==2.7.1
  • 我想可能是这样。不要传递枚举对象,而是传递例如StatusEnum.PROCESSED.name,它传递枚举对象的字符串名称。这样,您可以手动执行 SQLAlchemy 对普通枚举对象绑定值所做的操作。
猜你喜欢
  • 2012-04-30
  • 2019-03-17
  • 1970-01-01
  • 2018-07-13
  • 2021-11-19
  • 1970-01-01
  • 2010-10-07
  • 2012-11-24
  • 1970-01-01
相关资源
最近更新 更多