SQLAlchemy将具有聚合函数的列添加到动态加载程序列表(AppenderQuery)

Jos*_*uel 6 python sqlalchemy

我得到一个不正确的记录集,同时添加一个像func.sum动态关系一样的聚合函数.我在下面列出了一个示例代码来演示这一点.

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import (
    relationship,
    scoped_session,
    sessionmaker,
    backref
    )
from sqlalchemy import (
    create_engine,
    Table,
    Column,
    Integer,
    String,
    ForeignKey,
    func
)
from zope.sqlalchemy import ZopeTransactionExtension
import transaction

Base = declarative_base()
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))

class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    userid = Column(String(15), unique=True, nullable=False)
    article_list = relationship("Article", backref="user", lazy="dynamic")

class Tag(Base):
    __tablename__ = 'tags'

    id = Column(Integer, primary_key=True)
    name = Column(String(25), nullable=False, unique=True)

class Article(Base):
    __tablename__ = 'articles'

    id = Column(Integer, primary_key=True)
    title = Column(String(25), nullable=False)
    duration = Column(Integer)

    user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
    tags = relationship('Tag', secondary="tag_map",
                        backref=backref("article_list", lazy="dynamic"))


tag_map_table = Table(
    'tag_map', Base.metadata,
    Column('tag_id', Integer, ForeignKey('tags.id'), nullable=False),
    Column('article_id', Integer, ForeignKey('articles.id'), nullable=False))

engine = create_engine('sqlite:///tag_test.sqlite')
DBSession.configure(bind=engine)
Base.metadata.create_all(engine)

with transaction.manager:
    t1 = Tag(name='software')
    t2 = Tag(name='hardware')
    john = User(userid='john')

    a1 = Article(title='First article', duration=300)
    a1.user = john
    a1.tags.append(t1)
    a1.tags.append(t2)
    DBSession.add(a1)

    a2 = Article(title='Second article', duration=50)
    a2.user = john
    a2.tags.append(t1)
    a2.tags.append(t2)
    DBSession.add(a1)
Run Code Online (Sandbox Code Playgroud)

正如我们在上面的代码中看到的,我为这两篇文章添加了两个标记.现在我想查询用户'John'用标签分组写的文章以及它我想找到每个标签持续时间的总和.

john = DBSession.query(User).filter(User.userid=='john').first()

res =  john.article_list.join(Article.tags).add_column(
    func.sum(Article.duration)).group_by(Tag.id)
for article, tsum in res:
    print ("Article : %s, Sum duration : %d" % (article.title, tsum))
Run Code Online (Sandbox Code Playgroud)

生成的查询res

SELECT articles.id AS articles_id, articles.title AS articles_title, articles.duration AS articles_duration, articles.user_id AS articles_user_id, sum(articles.duration) AS sum_1 
FROM articles JOIN tag_map AS tag_map_1 ON articles.id = tag_map_1.article_id JOIN tags ON tags.id = tag_map_1.tag_id 
WHERE :param_1 = articles.user_id GROUP BY tags.id
Run Code Online (Sandbox Code Playgroud)

当直接在sqlite数据库上执行时,会产生对应于这两个标记的两行

2|Second article|50|1|350
2|Second article|50|1|350
Run Code Online (Sandbox Code Playgroud)

然而,SQLAlchemy返回的结果只反映了一行

文章:第二篇,总和持续时间:350

但是,如果我添加一个额外的列来包含AppenderQuery对象中的tag-name

res = john.article_list.join(Article.tags).add_column(Tag.name).add_column(
    func.sum(Article.duration)).group_by(Tag.id)

for article, tag_name, tsum in res:
    print ("Article : %s, Tag : %s, Sum duration : %d" % (
        article.title, tag_name, tsum))
Run Code Online (Sandbox Code Playgroud)

我得到了适当的结果

Article : Second article, Tag : software, Sum duration : 350
Article : Second article, Tag : hardware, Sum duration : 350
Run Code Online (Sandbox Code Playgroud)

那么,在AppenderQuery对象上使用聚合函数以获得分类结果的正确方法是什么?