如何在多主键表中插入具有自动增量id的行?

时间:2009-09-03 10:01:31

标签: python sql database sqlalchemy

我正在编写turbogears2应用程序。我有一张这样的桌子:

class Order(DeclarativeBase):
    __tablename__ = 'order'

    # id of order
    id = Column(Integer, autoincrement=True, primary_key=True)

    # buyer's id
    buyer_id = Column(Integer, ForeignKey('user.user_id',
        onupdate="CASCADE", ondelete="CASCADE"), primary_key=True)

我想在此表中插入一个新行,但我得到一个“字段'order_id'没有默认值”错误。似乎我必须手动设置订单的ID,因为我有两个主键。我的问题是,如何插入一个自动生成新ID的行?

如果我手动生成id,我遇到了一些问题。例如:

maxId = DBSession.query(func.max(Order)).one()[0]
newOrder = Order(id=maxId + 1, buyer_id=xxx)
DBSession.add(newOrder)

以这种方式添加新订单似乎没问题,但是,如果两个请求几乎同时运行这些代码,我们就遇到了一些问题。

如果有请求a和b按以下顺序运行此代码:

a.maxId = DBSession.query(func.max(Order)).one()[0]
b.maxId = DBSession.query(func.max(Order)).one()[0]
b.newOrder = Order(id=maxId + 1, buyer_id=xxx)
b.DBSession.add(newOrder)
a.newOrder = Order(id=maxId + 1, buyer_id=xxx)
a.DBSession.add(newOrder)

然后请求a可能失败,因为表中已存在具有相同id的订单。我可以抓住异常并再试一次。但我想知道,还有更好的方法吗?

有时,id不是简单的整数,我们可能需要这样的订单ID:

2009090133 2009-09-01第33号订单标准

在这种情况下,自动增量不可用。所以我别无选择,manualy为订单分配id。所以我的另一个问题是,有没有比捕获异常更好的方法,并重试插入一个带有id的行。

2 个答案:

答案 0 :(得分:1)

如果您想为每个买家提供订单的序号,那么您必须将插入一个买家的交易序列化。您可以通过获取买方行的独占锁来实现此目的:

sess.query(Buyer.id).with_lockmode('update').get(xxx)
order_id = sess.query(func.max(Order.id)+1).filter_by(buyer_id=xxx).scalar() or 1
sess.add(Order(id=order_id, buyer_id=xxx))

当两个交易尝试并行地为一个买方插入订单时使用此模式,其中一个将在第一行阻止,直到另一个交易完成或失败。

答案 1 :(得分:1)

您应该在列定义上使用默认值

id = Column(Integer, default = sqlexpression)

其中sqlexpression可以是sql表达式。这是documentation。对于自动增量,您应该使用sql表达式coalesce(select max(order.id) from order,0) + 1。为方便起见,您可以导入sqlalchemy.sql.text,以便id列看起来像

id = Column(Integer, default = text("coalesce(select max(order.id) from order,0) + 1"))
相关问题