【问题标题】:How to insert to parent and child table simultaneously in Flasksqlachemy?如何在 Flask Sqlalchemy 中同时插入父表和子表?
【发布时间】:2020-07-05 04:54:16
【问题描述】:

我将 FlaskSqlachemy 与 Postgresql 数据库一起使用。

我有一个名为 Order 的表和一个名为 Attempt(尝试交付订单)的表,它们是一对多的关系。尝试表有一个针对 order.id 的 FK。

我想在同一个会话中同时向 Order 和 Attempt 表中插入记录。问题是我在插入之前不知道 order.id。这是因为order.id是Order表的PK,具有自增功能。

我可以先插入订单,然后检查 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'))

【问题讨论】:

    标签: python database postgresql sqlalchemy flask-sqlalchemy


    【解决方案1】:

    我建议使用flush(),而不是使用commit() 将更改持久化到您的数据库中。 Flush 将更改推送到数据库事务缓冲区,即您的对象被创建并放置在缓冲区内,但并未真正作为记录写入您的数据库。之所以如此,是因为在该会话中,如果发生异常,例如插入命令失败,我们将使用 rollback() 方法回滚到以前的状态。


    这非常适合您的用例,因为当您创建对象并使用刷新方法时,您可以访问在数据库中自动递增的 id,在您的代码中进一步使用。遇到异常时不要忘记使用 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()
    

    【讨论】:

    • 谢谢!非常有帮助。我可以在第二次尝试块而不是第二次刷新上提交更改吗?关于 db.session.rollback(),我认为 Sqlalchemy 在这种情况下会自动回滚.. 很好的提示。谢谢
    • 是的,如果您确定您的用例或事务此时已结束并且想要将数据保存在您的数据库中,您可以使用提交而不是第二次刷新。对于回滚,如果确实存在异常并且您没有处理它,sqlalchemy 会回滚,但是由于您已经在这里处理了它,如果您在某个地方使用过提交,那么它将创建一个无效记录,所以最好是每当您处理异常时,请具体提及回滚。也使代码更简洁,并提高了代码的可读性。
    • 知道了。感谢您的帮助。
    猜你喜欢
    • 2023-03-08
    • 2012-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多