web-dev-qa-db-ja.com

SQLAlchemyで「or_」または「and_」を使用してもう少し複雑なフィルターを作成する方法

用語のリストから非常に簡単な検索をしようとしています

terms = ['term1', 'term2', 'term3']

プログラムで用語のリストを調べ、用語のリストから条件を作成して、filteror_または_andを使用してクエリを実行するにはどうすればよいですか?

query.filter(or_(#something constructed from terms))
27
Andrew Kou

用語のリストがあり、フィールドがそれらの1つと一致する行を検索する場合は、in_()メソッドを使用できます。

_terms = ['term1', 'term2', 'term3']
query.filter(Cls.field.in_(terms))
_

より複雑なことをしたい場合は、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 = ?
_

条件を表す句要素は、任意の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 = ?
_
28
Ants Aasma

terms変数に有効なSQLステートメントフラグメントが含まれていると仮定すると、アスタリスクが前に付いたtermsor_またはand_に渡すことができます。

>>> from sqlalchemy.sql import and_, or_
>>> terms = ["name='spam'", "email='[email protected]'"]
>>> print or_(*terms)
name='spam' OR email='[email protected]'
>>> print and_(*terms)
name='spam' AND email='[email protected]'

これは、termsvalidおよび適切にエスケープされたSQLフラグメントのみが含まれていることを前提としているため、悪意のあるユーザーがアクセスできる場合、これは潜在的に安全ではないことに注意してくださいtermsどういうわけか。

自分でSQLフラグメントを作成する代わりに、sqlalchemy.sqlの他のメソッドを使用してSQLAlchemyにパラメーター化されたSQLクエリを作成させる必要があります。テーブル用にTableオブジェクトを準備したかどうかはわかりません。その場合、usersのインスタンスであるTableという変数があり、データベース内のusersテーブルを記述していると想定します。次に、次のことを実行できます。

from sqlalchemy.sql import select, or_, and_
terms = [users.c.name == 'spam', users.c.email == '[email protected]']
query = select([users], and_(*terms))
for row in conn.execute(query):
    # do whatever you want here

ここで、users.c.name == 'spam'sqlalchemy.sql.expression._BinaryExpressionオブジェクトを作成します。これは、これがnameテーブルのusers列とspamを含む文字列リテラルとの間のバイナリ等価関係であることを記録します。このオブジェクトを文字列に変換すると、users.name = :1のようなSQLフラグメントが取得されます。ここで、:1はパラメーターのプレースホルダーです。 _BinaryExpressionオブジェクトは:1'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)
)

または、自動読み込みを使用して、データベースエンジンにデータベースの構造を照会し、usersを自動的に構築することもできます。明らかにこれはより時間がかかります:

users = Table('users', metadata, autoload=True)
17
Tamás

さて、私はここでまったく同じ問題を抱えていました: SQLAlchemy:主キーによる効率的/より良い選択?

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

あなたはこれを好きですか?

8
Hadrien

" Conjunctions "ドキュメントを使用して、条件AndOr、およびnotを組み合わせることができます。

0
PersianGulf