Запрос 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)

(получите вышеприведенный фрагмент бесплатно, если вы используете этот модуль сохранения шаблонов)