SQLAlchemy:条件自动增量

时间:2012-11-01 20:10:32

标签: python orm sqlalchemy relational-database

我想创建一个平面论坛,其中线程不是单独的表,并且有一个复合主键用于帖子。

所以帖子有两个字段形成一个自然键:thread_idpost_number,其中更多是它们所属的线程的ID,后者是它们在线程中的位置。如果你不相信,请检查下面的行。

我的问题是我不知道怎么告诉SQLAlchemy

  

在使用thread_id Post提交新的tid个实例时,查看有多少帖子存在thread_id tid,并自动增加该号码。


为什么我认为架构是一个好主意?因为它是自然而高效的:

class Post(Base):
    number    = Column(Integer, primary_key=True, autoincrement=False, nullable=False)
    thread_id = Column(Integer, primary_key=True, autoincrement=False, nullable=False)
    title     = Column(Text) #nullable for not-first posts
    text      = Column(Text, nullable=False)
    ...
PAGESIZE = 10
#test
tid = 5
page = 4

整个主题(查询):

thread5 = session.query(Post).filter_by(thread_id=5)

主题标题:

title = thread5.filter_by(number=0).one().title

主题页面

page4 = thread5.filter(
    Post.number >= (page    * PAGESIZE),
    Post.number < ((page+1) * PAGESIZE)).all()
#or
page4 = thread5.offset(page * PAGESIZE).limit(PAGESIZE).all()

页数:

ceil(thread5.count() / PAGESIZE)

2 个答案:

答案 0 :(得分:2)

您可以使用an SQL expression as a default value执行此操作(请参阅default参数)。给它一个这样的可调用:

from sqlalchemy.sql import func

def maxnumber_for_threadid(context):
    return post_table.select([func.max(post_table.c.number)]).where(post_table.c.thread_id==context.current_parameters['thread_id'])

我不完全确定你可以从默认的callable返回一个sql表达式 - 你可能必须实际执行这个查询并在回调中返回一个标量值。 (光标应该来自context参数。)

但是,我强烈建议您执行@kindall所说的操作,并为number列使用另一个自动递增序列。即使没有SQLAlchemy,你想要做的事情实际上是非常棘手的。例如,如果您使用的是MVCC数据库,则需要引入特殊的行级锁定,以便在运行事务时,匹配thread_id的行数不会更改。如何做到这一点取决于数据库。例如,使用MySQL InnoDB,您需要执行以下操作:

BEGIN TRANSACTION;
SELECT MAX(number)+1 FROM posts WHERE thread_id=? FOR UPDATE;
INSERT INTO posts (thread_id, number) VALUES (?, ?); -- number is from previous query
COMMIT;

如果您没有使用FOR UPDATE,那么可以想象另一个尝试同时在同一个帖子中插入新帖子的连接可能会为number获得相同的值。

因为需要额外的查询和锁定,所以后插入实际上相当慢(相对而言)而不是高性能。

所有这些都是通过使用单独的序列来解决的,而不用担心只在thread_id中增加帖子号。

答案 1 :(得分:0)

您应该使用一个全局帖子编号,该编号会增加任何帖子中的帖子。然后,您不需要找出用于给定线程的正确数字。然后,给定的线程可能具有编号为7,20,42,51的帖子,依此类推。这没关系,因为您可以从查询中获取的记录集大小轻松获取帖子中的帖子数量,并且您可以轻松地将HTML输出中的帖子与实际帖子编号分开。