2

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

所以帖子有两个字段形成一个自然键: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)
4

2 回答 2

2

您可能可以使用SQL 表达式作为默认值来执行此操作(请参阅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'])

我不确定您是否可以从默认可调用对象中返回一个 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;

If you didn't use FOR UPDATE, then conceivably another connection trying to insert a new post into the same thread at the same time could have gotten the same value for number.

So rather than being performant, post inserts are actually quite slow (relatively speaking) because of the extra query and locking required.

All this is resolved by using a separate sequence and not worrying about post number incrementing only within a thread_id.

于 2012-11-01T21:25:19.057 回答
0

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

于 2012-11-01T20:16:35.963 回答