sqlalchemy 绑定值

2 投票
1 回答
1592 浏览
提问于 2025-04-16 19:04

我想要动态地获取一个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里。你可以通过各种ClauseElementColumnElement类的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')]

撰写回答