Как мне создать немного более сложный фильтр, используя or_ или and_ в sqlalchemy - PullRequest
26 голосов
/ 21 апреля 2010

Я пытаюсь сделать очень простой поиск из списка терминов

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

Как программно просмотреть список терминов и построить «условия» из списка терминов, чтобы я мог выполнить запрос, используя фильтр и or_ или _and?

e.g. query.filter(or_(#something constructed from terms))

Ответы [ 4 ]

28 голосов
/ 21 апреля 2010

Если у вас есть список терминов и вы хотите найти строки, в которых поле соответствует одному из них, вы можете использовать метод 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 = ?
17 голосов
/ 21 апреля 2010

Предполагая, что ваша переменная 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'

Обратите внимание, что это предполагает, что terms содержит только допустимые и правильно экранированные фрагменты 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 == 'spam@eggs.com']
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. Когда вы преобразуете этот объект в строку, вы получите фрагмент SQL, например users.name = :1, где :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)
8 голосов
/ 21 апреля 2010

хорошо, у меня была такая же проблема: SQLAlchemy: эффективный / лучший выбор по первичным ключам?

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

Вам нравится это?

0 голосов
/ 01 сентября 2013

Вы можете использовать:

http://docs.sqlalchemy.org/en/rel_0_8/core/tutorial.html#conjunctions

В документации abobe вы найдете, как комбинировать условия. И, или нет ...!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...