Запрос sqlalchemy с флагом с ключевым словом в качестве переменной
Скажем, у меня есть такая модель:
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
hometown = db.Column(db.String(140))
university = db.Column(db.String(140))
Чтобы получить список пользователей из Нью-Йорка, это мой запрос:
User.query.filter_by(hometown='New York').all()
Чтобы получить список пользователей, которые идут в USC, это мой запрос:
User.query.filter_by(university='USC').all()
И чтобы получить список пользователей из Нью-Йорка и кто отправляется в USC, это мой запрос:
User.query.filter_by(hometown='New York').filter_by(university='USC').all()
Теперь я хотел бы динамически генерировать эти запросы на основе значения переменной.
Например, моя переменная может выглядеть так:
{'hometown': 'New York'}
Или вот так:
{'university': 'USC'}
... Или даже так:
[{'hometown': 'New York'}, {'university': 'USC'}]
Можете ли вы помочь мне написать функцию, которая принимает словарь (или список словарей) в качестве ввода, а затем динамически строит правильный запрос sqlalchemy?
Если я пытаюсь использовать переменную для ключевого слова, я получаю это err:
key = 'university'
User.query.filter_by(key='USC').all()
InvalidRequestError: Entity '<class 'User'>' has no property 'key'
Во-вторых, я не уверен, как динамически связывать несколько выражений filter_by.
Я могу явным образом вызывать выражение filter_by, но как я могу связать несколько элементов на основе переменной?
Надеюсь, это имеет больше смысла.
Спасибо!
Ответы
Ответ 1
SQLAlchemy filter_by
принимает аргументы ключевого слова:
filter_by (** kwargs)
Другими словами, функция позволит вам дать ему любой параметр ключевого слова. Вот почему вы можете использовать любое ключевое слово, которое вы хотите в своем коде: SQLAlchemy в основном рассматривает аргументы в словаре значений. Дополнительную информацию о аргументах ключевых слов см. В учебнике Python.
Таким образом, разработчики SQLAlchemy могут получать произвольную связку аргументов ключевого слова в форме словаря. Но вы просите об обратном: можете ли вы передать произвольную связку аргументов ключевого слова функции?
Оказывается, что в Python вы можете использовать функцию unpacking. Просто создайте словарь аргументов и передайте его функции, которой предшествует **
, например:
kwargs = {'hometown': 'New York', 'university' : 'USC'}
User.query.filter_by(**kwargs)
# This above line is equivalent to saying...
User.query.filter_by(hometown='New York', university='USC')
Ответ 2
filter_by(**request.args)
не работает, если у вас есть немодельные параметры запроса, например page
для разбивки на страницы, в противном случае вы получите такие ошибки:
InvalidRequestError: Entity '<class 'flask_sqlalchemy.MyModelSerializable'>' has no property 'page'
Я использую что-то вроде этого, которое игнорирует параметры запроса не в модели:
builder = MyModel.query
for key in request.args:
if hasattr(MyModel, key):
vals = request.args.getlist(key) # one or many
builder = builder.filter(getattr(MyModel, key).in_(vals))
if not 'page' in request.args:
resources = builder.all()
else:
resources = builder.paginate(
int(request.args['page'])).items
Учитывая модель с столбцом под названием valid
, что-то вроде этого будет работать:
curl -XGET "http://0.0.0.0/mymodel_endpoint?page=1&valid=2&invalid=whatever&valid=1"
invalid
будет проигнорирован, а page
доступен для разбивки на страницы и, самое главное, будет создан следующий SQL: WHERE mymodel.valid in (1,2)
(получите вышеприведенный фрагмент бесплатно, если вы используете этот модуль сохранения шаблонов)