Как обрабатывать уникальные данные в SQLAlchemy, фляге, Pyhon - PullRequest
0 голосов
/ 29 августа 2018

Как вы обычно обрабатываете уникальные записи базы данных в Flask? У меня есть следующий столбец в моей модели БД:

bank_address = db.Column(db.String(42), unique=True)

Проблема в том, что даже прежде чем я могу проверить, находится ли он уже в базе данных или нет, я получаю сообщение об ошибке:

Проверьте, является ли он уникальным, и ТО затем запишите в БД:

if request.method == 'POST':
    if user.bank_address != request.form['bank_address_field']:
        user.bank_address = request.form['bank_address_field']
        db.session.add(user)
        db.session.commit()

Я получаю ошибку:

sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) УНИКАЛЬНО ограничение не выполнено: user.bank_address_field [SQL: 'ОБНОВЛЕНИЕ пользователя SET bank_address_field =? ГДЕ user.id =? ']

1 Ответ

0 голосов
/ 29 августа 2018

Вы можете сделать одну из двух вещей:

  • Сделать запрос для пользователей с этим полем:

    if User.query.filter(User.bank_address == request.form['bank_address_field']).first():
        # error, there already is a user using this bank address
    

    Однако здесь есть большая проблема, см. Ниже.

  • Поймать исключение:

    from sqlalchemy.exc import IntegrityError
    
    try:
        db.session.commit()
    except IntegrityError:
        db.session.rollback()
        # error, there already is a user using this bank address or other
        # constraint failed
    

    , где IntegrityError можно импортировать из sqlalchemy.exc. Как только возникает IntegrityError, независимо от того, поймали ли вы ошибку или нет, сеанс, в котором вы работали, становится недействительным. Чтобы продолжить использовать сессию, вам нужно ввести db.session.rollback().

Последнее лучше, потому что на не распространяются условия гонки . Представьте, что два пользователя пытаются зарегистрировать один и тот же адрес банка одновременно:

  • Пользователь A отправляет, User.query.filter().first() возвращает None, потому что никто еще не использует адрес.
  • Почти одновременно, пользователь B отправляет, User.query.filter().first() возвращает None, потому что никто еще не использует адрес.
  • Адрес банка пользователя А записывается в базу данных, успешно
  • Банковский адрес пользователя B не может быть записан в базу данных, потому что проверка целостности не удалась, так как пользователь A только что записал этот адрес.

Так что просто перехватывает исключение , потому что транзакции базы данных гарантируют, что база данных 1043 * заблокирует таблицу перед проверкой ограничения и добавлением или обновлением пользователя.

Вы также можете заблокировать всю таблицу во Flask, но Python общается с базой данных намного медленнее. Если у вас загруженный сайт, вы не хотите, чтобы обновление базы данных происходило медленно, в результате многие пользователи ожидают снятия блокировки. Вы хотите, чтобы блокировка была минимальной и как можно более короткой, и чем ближе к фактическим данным, которые вы блокируете, тем раньше вы сможете снова снять блокировку. Базы данных очень хороши в такого рода блокировках и очень близки к своим данным (естественно), поэтому оставьте блокировку для базы данных и используйте вместо этого исключение.

...