我正在尝试在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)
当我尝试将负整数绑定到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
,而不是在提交时?
答案 0 :(得分:0)
这是使用inspect
模块的解决方案。由于它使用了inspect模块,因此对于SA升级(如文档字符串中所述)可能不是很可靠。
此代码假定该值可作为变量key
在TypeDecorator.process_bind_param
上方两个堆栈级别使用(因此[3]
中的索引get_column_name
)。如果SA内部的堆栈深度或变量名称发生更改,则此代码将中断,这就是为什么它使用“裸除”的原因。但是,该异常将记录为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