我正在编写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的行。
答案 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"))