Python SQLAlchemy:条件自动增量

Python SQLAlchemy:条件自动增量,python,orm,sqlalchemy,relational-database,Python,Orm,Sqlalchemy,Relational Database,我想创建一个平面论坛,其中线程不是单独的表,帖子有一个复合主键 因此,post有两个字段组成一个自然键:thread_id和post_number,这两个字段是它们所属线程的id,后者是它们在线程中的位置。如果你不相信,检查下面的线 我的问题是我不知道如何告诉你炼金术 提交添加线程id为tid的新帖子实例时,请查找存在线程id为tid的帖子的数量,并从该数量开始自动递增 为什么我认为模式是个好主意?因为它是自然的,性能良好: class Post(Base): number = C

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

因此,post有两个字段组成一个自然键:thread_id和post_number,这两个字段是它们所属线程的id,后者是它们在线程中的位置。如果你不相信,检查下面的线

我的问题是我不知道如何告诉你炼金术

提交添加线程id为tid的新帖子实例时,请查找存在线程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()

您应该只使用一个在任何线程中递增的全局帖子编号。然后,您不需要为给定的线程找出正确的编号。那么,给定的线程可能有编号为7、20、42、51等的帖子。这并不重要,因为您可以通过查询返回的记录集的大小轻松获得线程中的帖子数量,并且可以轻松地将HTML输出中的帖子数量与实际帖子数量分开进行编号。

您可能可以通过查看默认参数来完成此操作。给它一个这样的调用:

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'])
我不确定您是否可以从默认的可调用函数返回sql表达式,您可能需要实际执行此查询并在回调函数中返回标量值。光标应从上下文参数中可用

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

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'])
开始交易; 从thread_id=?的帖子中选择MAXnumber+1?更新; 在帖子中插入线程_id,数值?,?;-编号来自上一个查询 犯罪 如果您没有使用更新,那么可以想象,另一个连接试图在同一时间将新帖子插入同一个线程时,可能会得到相同的number值

因此,由于需要额外的查询和锁定,post插入实际上相对来说相当慢,而不是性能


所有这些问题都可以通过使用一个单独的序列来解决,而不必担心只在一个线程id内增加post编号。

这是每个人都会做的,但它似乎太武断了。我特别想使用已经存在的信息,而不是引入任意的附加ID。因为post编号实际上意味着什么,post ID不会有人关心订单总量是在哪个位置发布的,如果有人这样做了,我们只需要按创建日期进行排序!谢谢你的回答,即使你真的认为另一种方式会更好。但是,虽然我同意另一种方法更容易实现,但我仍然认为在一般情况下,它并不昂贵:一个事务通常只应创建一个帖子,因此唯一的要求是在每个帖子的一个事务中执行您发布的查询/插入。