SQLAlchemy按映射表中的关系顺序排序

时间:2012-09-02 15:32:44

标签: sqlalchemy

所以在阅读SQLAlchemy ordering by count on a many to many relationship之后,我试图复制结果,但它无法正常工作。所以我的模型是,

class Group(Base):
__tablename__='groups'
__table_args__={
    'mysql_engine':'InnoDB',
    'mysql_charset':'utf8',
}

id = Column(Integer, primary_key=True, unique=True)
name = Column(VARCHAR(30), primary_key=True, unique=True)
time = Column(DateTime, onupdate = datetime.datetime.now)
description = Column(VARCHAR(255))
creator_id = Column(Integer, ForeignKey('users.id'))
privacy = Column(SMALLINT) # 0 == public, 1 == friends, 2 == private

def __init__(self, name, descr, creator, privacy):
    self.name = name
    self.description = descr
    self.creator_id = creator
    self.privacy = privacy

class GroupUserRelationship(Base):
__tablename__='groupUserRelationships'
__table_args__={
    'mysql_engine':'InnoDB',
    'mysql_charset':'utf8',
}

id = Column(Integer, primary_key = True)
group_id = Column(Integer, ForeignKey('groups.id'))
user_id = Column(Integer, ForeignKey('users.id'))
time = Column(DateTime, onupdate=datetime.datetime.now)

def __init__(self, group, user):
    self.group_id = group
    self.user_id = user

我的sqlalchemy查询是groups = session.query(Group, func.count(GroupUserRelationship.user_id).label('total')).join(GroupUserRelationship).group_by(Group).order_by('total DESC').limit(20).all(),但是当我尝试迭代它返回的列表并访问组ID时,我得到一个AttributeError:'NamedTuple'没有属性id。怎么回事?

1 个答案:

答案 0 :(得分:2)

此表单的查询:

session.query(Group, func.count(GroupUserRelationship.user_id).label('somelabel'))

将返回这样的元组列表:

[
    (group1, 5),
    (group2, 7),
    ...
]

..等等。

到达group.id的迭代是:

for group, user_id in session.query(Group, func.count(GUR.user_id).label('somelabel')).join(...):
    print group.id

对于计数,我认为第一种重要的技术是养成不按整行划分的习惯(即group_by(Group))。虽然这里的查询可以使用该技术工作,但这是不好的做法,因为您正在使数据库执行大量与整个组表的所有列匹配的额外工作,而实际上您需要组合的只是单列GroupUserRelationship.user_id 。我参考了这篇文章http://weblogs.sqlteam.com/jeffs/archive/2005/12/14/8546.aspx,对此进行了一些阐述。然后,SQLAlchemy教程在此处提供了此表单的示例:http://docs.sqlalchemy.org/en/rel_0_7/orm/tutorial.html#using-subqueries

在SQLAlchemy中,下一个非常有用的功能是使用relationship()在两个类之间建立特定的连接路径。所以这就是,使用子查询完成分组表达式。这里使用的特殊技巧是可选的,你可以说join(subquery, Group.gur)这意味着“使用Group.gur关系的等效连接条件加入到这个子查询”。

编辑

以说明完整的往返示例

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Group(Base):
    __tablename__ = 'groups'

    id = Column(Integer, primary_key=True)
    name = Column(VARCHAR(30))
    gur = relationship("GroupUserRelationship")

class GroupUserRelationship(Base):
    __tablename__ = 'groupUserRelationships'

    id = Column(Integer, primary_key=True)
    group_id = Column(Integer, ForeignKey('groups.id'))

e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)

s = Session(e)

s.add_all([
    Group(name='g1', gur=[GroupUserRelationship() for i in xrange(3)]),
    Group(name='g2', gur=[GroupUserRelationship() for i in xrange(8)]),
    Group(name='g3', gur=[GroupUserRelationship() for i in xrange(5)]),
    Group(name='g4', gur=[GroupUserRelationship() for i in xrange(1)]),
    Group(name='g5', gur=[GroupUserRelationship() for i in xrange(2)]),
])
s.commit()


gur_count = s.query(
                func.count(GroupUserRelationship.id).label('total'),
                GroupUserRelationship.group_id
            ).group_by(GroupUserRelationship.group_id).\
            subquery()

for group, gur_count in s.query(Group, gur_count.c.total).\
            join(gur_count, Group.gur).\
            order_by(gur_count.c.total):
    print "GROUP:", group.name, "GROUP ID:", group.id, "NUMBER OF GUR:", gur_count

输出(减去SQL回显,这对于查看正在发生的事情非常有用):

GROUP: g4 GROUP ID: 4 NUMBER OF GUR: 1
GROUP: g5 GROUP ID: 5 NUMBER OF GUR: 2
GROUP: g1 GROUP ID: 1 NUMBER OF GUR: 3
GROUP: g3 GROUP ID: 3 NUMBER OF GUR: 5
GROUP: g2 GROUP ID: 2 NUMBER OF GUR: 8