python - 在sqlalchemy中使用自定义列类型时如何查找列名

标签 python sqlalchemy flask-sqlalchemy

我正在尝试在 sqlalchemy 中定义我自己的列类型:

class NonNegativeInt(TypeDecorator):
    impl = Integer

    def process_bind_param(self, value, dialect):
        if value is not None:
            if not isinstance(value, int) or value < 0:
                raise TypeError('Expect non-negative integer, find %s instead.' % value)
        return value

class Game(Model):
    score = Column(NonNegativeInt, default=0)

当我尝试将负整数绑定(bind)到 NonNegativeInt 列时,例如分数,它会按预期引发错误:

sqlalchemy.exc.StatementError: (exceptions.TypeError) Expect non-negative integer, find -1 instead.

但是没有指定列的名称,所以当列很多的时候,调试起来不太方便。使用原始 Integer 类型时,我得到了更具体的错误消息:

sqlalchemy.exc.InternalError: (pymysql.err.InternalError) (1366, u"Incorrect integer value: 'aaaa' for column 'score' at row 1")

如何找到有错误的列的名称(例如分数)?此外,是否可以在分配错误值时引发错误:game.score = -1,而不是提交时?

最佳答案

这是一个使用 inspect 模块的解决方案。由于它使用检查模块,因此它可能对 SA 升级不是很稳健(如文档字符串中所述)。

此代码假定该值作为变量 keyTypeDecorator.process_bind_param 之上两个堆栈级别可用(因此索引 [3]get_column_name 中)。如果堆栈深度或变量名称在 SA 内部发生变化,则此代码将中断,这就是它使用“bare except”的原因。但是,异常将被记录为包含堆栈跟踪的 DEBUG 消息,因此它应该保持可调试状态。

不过,我确实认为 SA 的那部分不太可能发生变化。

import logging

LOG = logging.getLogger(__name__)


def get_column_name() -> str:
    """
    Tries to determine the field/column-name from inside a TypeDecorator
    method.

    This is done by inspecting the current Python stack and may not always work
    and is not guaranteed to survive SQLAlchemy upgrades. In that case, it will
    return ``"<unknown>"`` as value so you should not use this for any other
    use-case than debugging or logging!
    """
    from inspect import getmembers, getouterframes, currentframe
    frame = currentframe()
    column_name = '<unknown>'
    try:
        target_frame = getouterframes(frame)[3]
        frame_locals = dict(getmembers(target_frame))['frame'].f_locals
        column_name = frame_locals['key']
    except:  # pylint: disable=bare-except
        LOG.debug('Unable to retrieve the column name', exc_info=True)
    return column_name

然后可以在 TypeDecorator 中使用此函数:

class NonNegativeInt(TypeDecorator):
    impl = Integer

    def process_bind_param(self, value, dialect):
        column = get_column_name()
        if value is not None:
            if not isinstance(value, int) or value < 0:
                raise TypeError('Expect non-negative integer in column %r, find %s instead.' % (column, value))
        return value

关于python - 在sqlalchemy中使用自定义列类型时如何查找列名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37697516/

相关文章:

python - 如何用python循环一个范围

python - Python 线程什么时候快?

python - 如何将 SQLAlchemy 查询传递给execute(query_str, params) 函数

python - 属性错误 : 'int' object has no attribute '_sa_instance_state'

python - 从组合列表中选择对的最佳策略

python - 我可以让 pdb 立即开始调试吗?

python - sqlalchemy "after_update"事件后级联更新

python - 如何设置SQLAlchemy方言?

python - 在 SQLAlchemy 中以 dict 形式检索查询结果

python - 曾孙的 SQLAlchemy 链接关联代理?