Flask-SQLAlchemy проверяет наличие строки в таблице

У меня есть приложение Flask, которое использует Flask-SQLAlchemy для подключения к базе данных MySQL.

Я хотел бы иметь возможность проверить, присутствует ли строка в таблице. Как мне изменить такой запрос, чтобы проверить, существует ли строка:

db.session.query(User).filter_by(name='John Smith')

Я нашел решение на этом вопросе, которое использует SQLAlchemy, но не похоже на то, как работает Flask-SQLAlchemy:

from sqlalchemy.sql import exists    
print session.query(exists().where(User.email == '...')).scalar()

Спасибо.

Ответ 1

Поскольку вы хотите видеть только, существует ли пользователь, вы не хотите запрашивать весь объект. Просто запросите идентификатор, он существует, если скалярное значение не равно None.

exists = db.session.query(User.id).filter_by(name='davidism').scalar() is not None
SELECT user.id AS user_id 
FROM user 
WHERE user.name = ?

Второй показанный вами запрос также работает нормально, Flask-SQLAlchemy ничего не делает для предотвращения запросов любого типа, которые может выполнять SQLAlchemy. Это возвращает False или True вместо None или id как выше, но это немного дороже, потому что он использует подзапрос.

exists = db.session.query(db.exists().where(User.name == 'davidism')).scalar()
SELECT EXISTS (SELECT * 
FROM user 
WHERE user.name = ?) AS anon_1

Ответ 2

Оберните .exists() запрос в другой session.query() с scalar() вызова в конце. SQLAlchemy создаст оптимизированный запрос EXISTS который возвращает True или False.

exists = db.session.query(
    db.session.query(User).filter_by(name='John Smith').exists()
).scalar()
SELECT EXISTS (SELECT 1 
FROM user 
WHERE user.name = ?) AS anon_1

Хотя это потенциально более дорогое из-за подзапроса, оно более ясно о том, что запрашивается. Это также может быть предпочтительнее, чем db.exists().where(...) потому что он выбирает константу вместо полной строки.

Ответ 3

Простите захват, но... с инструкциями, приведенными здесь, я сделал следующий валидатор WTForm для проверки уникальности поля

class Unique(object):
    def __init__(self, column, session, message="Already exists."):
        self.column = column
        self.session = session
        self.message = message

    def __call__(self, form, field):
        if field.data == field.object_data:
            return  # Field value equals to existing value. That ok.
        model = self.column.class_
        query = model.query.filter(self.column == field.data).exists()
        if self.session.query(query).scalar():
            raise ValidationError(self.message)

Он будет использоваться как этот

class Register(Form):
    email = EmailField('Email', [Unique(User.email, db.session)])

Однако, я хотел бы иметь API, который не нуждается в сеансе db как второй параметр

class Register(Form):
    email = EmailField('Email', [Unique(User.email)])

Есть ли способ получить сеанс db из модели? Без сеанса кажется невозможным избежать загрузки всего объекта, чтобы проверить его существование.

Ответ 4

bool(User.query.filter_by(name='John Smith').first())

Он вернет "False", если объекты с таким именем не существуют, и "True", если он существует.

Ответ 5

Думаю, что в давидизме есть опечатка, это работает для меня:

exists = db.session.query(**User**).filter_by(name='davidism').scalar() is not None