Как вставить в родительскую и дочернюю таблицу одновременно в Flasksqlachemy? - PullRequest
0 голосов
/ 24 марта 2020

Я использую FlaskSqlachemy с Postgresql базой данных.

У меня есть таблица с именем Order и таблица с именем Attempt (попытка доставить заказ) в отношении один ко многим. Таблица попыток имеет FK для order.id.

Я хочу вставить записи в таблицу Order и Attempt в одном сеансе. Проблема в том, что я не знаю order.id, прежде чем вставить его. Это потому, что order.id - это PK таблицы Order с функцией автоинкремента.

Я могу сначала вставить заказ, затем проверить order.id и, наконец, вставить попытку. Моя проблема: если вторая вставка не удалась, я получаю заказ, с которым не было предпринято никаких попыток.

Каков наилучший метод в этих случаях?

Ниже приведена упрощенная версия моих моделей БД:

class Order(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    attempts = db.relationship('Attempt', backref='order', lazy='select',
                               order_by="Attempt.date")

class Attempt(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    order_id = db.Column(None, db.ForeignKey('order.id'))

А ниже приведено мое решение:

    # Create an order object
    new_order = Order()

    # Try to add it to db
    try:
        db.session.add(new_order)
        db.session.commit()
    except Exception:
        ....
        return redirect(url_for('scheduleorder'))
    else:
        order = Order.query.filter_by(token=token).first()

        # Create an attempt object
        new_attempt = Attempt(order_id=order.id)
        try:
            db.session.add(new_attempt)
            db.session.commit()
        except Exception:
            ...
            return redirect(url_for('scheduleorder'))
        else:
            ...
            return redirect(url_for('scheduleorder'))

1 Ответ

1 голос
/ 26 марта 2020

Вместо использования commit(), который сохраняет изменения в вашей базе данных, я бы предложил использовать flush(). Flu sh вносит изменения в транзакционный буфер базы данных, т. Е. Ваш объект создается и помещается в буфер, но на самом деле не записывается в вашу базу данных как запись. Это так, потому что в дальнейшем в этом сеансе, если возникает исключение, как в вашем случае, если команда вставки не удалась, мы будем использовать метод rollback() для отката к предыдущему состоянию.


Это идеально подходит для вашего варианта использования, поскольку, когда вы создаете объект и используете метод flu sh, вы можете получить доступ к идентификатору, который был автоматически инкрементирован в БД, в вашем коде для дальнейшего использования. линия. При возникновении исключения не забудьте использовать db.session.rollback () для фактического отката транзакции.

# Create an order object
    new_order = Order()

    # Try to add it to db
    try:
        db.session.add(new_order)
        db.session.flush()
        order_id = new_order.id

    except Exception:
        ....
        db.session.rollback()
        return redirect(url_for('scheduleorder'))
    else:

        # Create an attempt object
        new_attempt = Attempt(order_id=order_id)
        try:
            db.session.add(new_attempt)
            db.session.flush()
        except Exception:
            ...
            db.session.rollback()
            return redirect(url_for('scheduleorder'))
        else:
            ...
            return redirect(url_for('scheduleorder'))

    # use the commit at the end of you usecase so that you actually create a record in your database.
    db.session.commit()
...