sqlalchemy 分页

5 投票
2 回答
11334 浏览
提问于 2025-04-16 19:11

我正在用Flask和SQLAlchemy构建一个REST应用,遇到了一个问题。我想查询所有用户以及他们的书籍数量。每个用户都有很多书,所以我的查询应该返回每个用户拥有的书籍数量。

//      Models
class User( object ):
    __tablename__ = 'user'

class Book( object ):
    __tablename__ = 'book'

//      Metadata
users_table = Table( 'user', metadata,
    Column( 'id', Integer, primary_key = True ),
    Column( 'username', String( 50 ), unique = True )
)

books_table = Table( 'book', metadata,
    Column( 'id', Integer, primary_key = True ),
    Column( 'title', String( 50 ) ),
    Column( 'user_id', Integer, ForeignKey( 'user.id' ) )
)

//      Mappers
mapper( User, users_table, properties = {
    'booksCount': column_property( 
        select( 
            [func.count( books_table.c.id )],
            books_table.c.user_id == users_table.c.id
        ).label( 'booksCount' )
    ),
    'books' : relationship( Book )
} )

mapper( Book, books_table, properties = {
    'user': relationship( User )
} )

如果我想查询所有用户,这个查询可以正常工作,并且会返回带有'booksCount'(书籍数量)的结果,这样没问题。但是如果我想更进一步,比如只查询那些'booksCount'大于4的用户,这就变得复杂了,因为我还需要在应用限制和偏移之前知道总的结果数量,这样我的分页功能才能正常工作。

//  this works
rows = User.query
totalRows = rows.count ()
users = rows.limit( 50 ).offset( 0 ).all()

for user in users:
    ...

//  this throws an error
rows = User.query.having('booksCount>4')
totalRows = rows.count ()
users = rows.limit( 50 ).offset( 0 ).all()

第二个例子出错的原因是因为 totalRows = rows.count () 这行代码会创建一个新的查询来计算结果的数量:SELECT count(1) AS count_1 FROM user。但是当在查询中加入 having 时,它就变成了 SELECT count(1) AS count_1 FROM user having booksCount>4,这显然会出错,因为在这个第二个查询中并没有选择booksCount。

那么,我该如何在应用了 having 子句的选择中提取总行数呢?

谢谢。

2 个回答

2

这其实是一个SQL语法错误。having 这个部分只有在你同时使用了 group by 的时候才能用,而且它只用来过滤聚合结果。你需要的是一个简单的 where 语句,可以这样写:

rows = User.query.filter(User.booksCount > 4)

顺便提一下,请按照Python的标准来写代码,比如 User.books_count

0

我来给自己解答一下,顺便把解决办法分享给其他遇到同样问题的人。如果你在用Ubuntu系统,并且是从软件库安装的sqlalchemy,建议你先把它卸载掉,然后去sqlalchemy的官方网站,按照那里的说明重新安装。现在的新版本(目前是0.7.1)已经修复了这个问题。

Ubuntu自带的是0.6.4版本。

撰写回答