如何使用sqlalchemy中的or_或and_构建稍微复杂的过滤器

And*_*Kou 26 python sqlalchemy filter

我正在尝试从术语列表中进行非常简单的搜索

terms = ['term1', 'term2', 'term3']
Run Code Online (Sandbox Code Playgroud)

如何以编程方式浏览术语列表并从术语列表构造"条件",以便我可以使用filter和or_或_and进行查询?

e.g. query.filter(or_(#something constructed from terms))
Run Code Online (Sandbox Code Playgroud)

Ant*_*sma 28

如果您有一个术语列表,并希望找到一个字段与其中一个匹配的行,那么您可以使用in_()方法:

terms = ['term1', 'term2', 'term3']
query.filter(Cls.field.in_(terms))
Run Code Online (Sandbox Code Playgroud)

如果你想要做更复杂的东西,然后or_()and_()ClauseElement对象作为参数.ClauseElement及其子类基本上代表查询的SQL AST.通常,您可以通过在Column或InstrumentedAttribute对象上调用比较运算符来创建子句元素:

# Create the clause element
clause = (users_table.columns['name'] == "something")
#    you can also use the shorthand users_table.c.name

# The clause is a binary expression ...
print(type(clause))
#    <class 'sqlalchemy.sql.expression._BinaryExpression'>
# ... that compares a column for equality with a bound value.
print(type(clause.left), clause.operator, type(clause.right))
#    <class 'sqlalchemy.schema.Column'>, <built-in function eq>,
#    <class 'sqlalchemy.sql.expression._BindParamClause'>

# str() compiles it to SQL
print(str(clause)) 
# users.name = ?

# You can also do that with ORM attributes
clause = (User.name == "something")
print(str(clause))
# users.name = ?
Run Code Online (Sandbox Code Playgroud)

您可以像处理任何Python对象一样处理表示条件的子句元素,将它们放入列表中,将它们组合成其他子句元素等等.因此,您可以执行以下操作:

# Collect the separate conditions to a list
conditions = []
for term in terms:
    conditions.append(User.name == term)

# Combine them with or to a BooleanClauseList
condition = or_(*conditions)

# Can now use the clause element as a predicate in queries
query = query.filter(condition)
# or to view the SQL fragment
print(str(condition))
#    users.name = ? OR users.name = ? OR users.name = ?
Run Code Online (Sandbox Code Playgroud)


Tam*_*más 17

假设您的terms变量包含有效的SQL语句片段,您可以简单地将terms前面的星号传递给or_and_:

>>> from sqlalchemy.sql import and_, or_
>>> terms = ["name='spam'", "email='spam@eggs.com'"]
>>> print or_(*terms)
name='spam' OR email='spam@eggs.com'
>>> print and_(*terms)
name='spam' AND email='spam@eggs.com'
Run Code Online (Sandbox Code Playgroud)

请注意,这假定terms只包含有效正确转义的 SQL片段,因此如果恶意用户可以terms以某种方式访问,这可能是不安全的.

您应该让SQLAlchemy使用其他方法构建参数化SQL查询,而不是自己构建SQL片段sqlalchemy.sql.我不知道你是否Table为桌子准备了物品; 如果是这样,假设你有一个叫做变量users这是一个实例Table,它描述你的users数据库中的表.然后,您可以执行以下操作:

from sqlalchemy.sql import select, or_, and_
terms = [users.c.name == 'spam', users.c.email == 'spam@eggs.com']
query = select([users], and_(*terms))
for row in conn.execute(query):
    # do whatever you want here
Run Code Online (Sandbox Code Playgroud)

这里,users.c.name == 'spam'将创建一个sqlalchemy.sql.expression._BinaryExpression对象,该对象记录这是表的nameusers与包含的字符串文字之间的二进制相等关系spam.将此对象转换为字符串时,您将获得一个SQL片段users.name = :1,其中:1包含参数的占位符.该_BinaryExpression对象还会记住:1to 的绑定'spam',但在执行SQL查询之前不会插入它.插入时,数据库引擎将确保正确转义它.建议阅读:SQLAlchemy的运算符范例

如果您只有数据库表但没有users描述该表的变量,则可以自己创建:

from sqlalchemy import Table, MetaData, Column, String, Boolean
metadata = MetaData()
users = Table('users', metadata,
    Column('id', Integer, primary_key=True),
    Column('name', String),
    Column('email', String),
    Column('active', Integer)
)
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用自动加载来查询数据库引擎以获取数据库的结构并users自动构建; 显然这更耗时:

users = Table('users', metadata, autoload=True)
Run Code Online (Sandbox Code Playgroud)


Had*_*ien 8

我在这里遇到了同样的问题: SQLAlchemy:主键有效/更好的选择?

terms = ['one', 'two', 'three']
clauses = or_( * [Table.field == x for x in terms] )
query = Session.query(Table).filter(clauses)
Run Code Online (Sandbox Code Playgroud)

你喜欢吗?