sqlalchemy 绑定值
我想要动态地获取一个sqlalchemy查询对象中的参数值:
q = session.query(Model).filter(Model.foo = 6)
我现在想从q
中获取值6
assert(q.magic == 6)
尝试:
print(q._criterion) # -> models.foo = :foo_1
但是foo_1
的值在哪里呢?
1 个回答
14
SQLAlchemy会根据你的过滤条件生成一个树状结构,把每个叶子节点适当地添加上去,最后把结果放在Query._criterion
里。你可以通过各种ClauseElement
和ColumnElement
类的get_children()
方法来查看这个结构。
比如说对于Model.foo == 6
,你会得到类似这样的结构:
Model.foo == 6
|
_BinaryExpression
/ \
/ \
/ \
Column('foo', Integer(), _BindParamClause(u'%(168261004 foo)s',
...) 6, type_=Integer())
如果你把两个条件用&符号连接起来,比如(Model.foo == 6) & (Model.name == 'a name')
,或者通过连续调用filter
,你会得到一个BooleanClauseList
,里面有两个_BinaryExpression
的子节点。这意味着你不能简单地写一个固定的表达式来可靠地返回你想要的值,而是需要遍历这个条件树。
sqlalchemy.sql.visitors
里的traverse
函数正是用来做这个的,它依赖一个字典,这个字典把每个元素的__visit_name__
属性和一个处理函数关联起来。这是一种广度优先的遍历方式,正如你在下面的例子中看到的那样;另外还有深度优先的版本可供选择。
下面的函数展示了如何从给定的查询中生成一个列-参数对的列表。我是从Beaker缓存示例中改编过来的:
def extract_cols_params(query):
if query._criterion is None:
return []
c, v = [], []
def visit_bindparam(bind):
value = query._params.get(bind.key, bind.value)
if callable(value):
value = value()
v.append(value)
def visit_column(col):
c.append('%s.%s' % (col.table.name, col.name))
visitors.traverse(query._criterion, # our predicate tree
{}, # kwargs for the iterator used by
# the traversal; undeeded.
{'bindparam': visit_bindparam, # for _BindParamClauses
'column' : visit_column}) # for Columns
return zip(c, v)
>>> extract_cols_params(Session.query(Model).filter((Model.foo == 6)
).filter(Model.name == 'a name'))
[('models.foo', 6), ('models.name', 'a name')]