所以在阅读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。怎么回事?
答案 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