Python 在SQLAlchemy中按自引用子集合的长度对对象排序

Python 在SQLAlchemy中按自引用子集合的长度对对象排序,python,sqlalchemy,Python,Sqlalchemy,我有一个表示层次结构的数据库表,这意味着它有一个自引用外键。我想按孩子的数量对我的物品进行分类 问题是,我既不知道如何进行适当的自联接,也不知道如何在原始查询中请求子集合的计数。结果是,我不得不求助于检索子对象,获取子集合长度,并用Python对结果进行排序 from sqlalchemy import * from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker

我有一个表示层次结构的数据库表,这意味着它有一个自引用外键。我想按孩子的数量对我的物品进行分类

问题是,我既不知道如何进行适当的自联接,也不知道如何在原始查询中请求子集合的计数。结果是,我不得不求助于检索子对象,获取子集合长度,并用Python对结果进行排序

from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
Base = declarative_base()

engine = create_engine("...")
Session = sessionmaker(bind=engine)
session = Session()

class Variable(Base):
    __tablename__ = 'variable'

    id          = Column(Integer, primary_key=True)
    parent_id   = Column(Integer, ForeignKey('variable.id'))
    parent = relationship('Variable', remote_side=[id], backref="children")

# Works fine
for v in session.query(Variable).all():
    print(len(v.children))

# Works fine
for v in session.query(Variable.id).all():
    print(v)

# AttributeError: type object 'Variable' has no attribute 'children'
for v in session.query(func.count(Variable.children)).all():
    print(v)

# AttributeError: type object 'Variable' has no attribute 'children'
for v in session.query(Variable.children).all():
    print(v)
它似乎认为它不知道儿童的情况,而只知道特定的情况。作为一个实验,我尝试显式地添加
子对象

children = relationship('Variable', backref="parent")
我得到了以下错误:

Error creating backref 'parent' on relationship 'Variable.children': property of that name exists on mapper 'Mapper|Variable|variable'
下面解决了这个问题,但这是一个暴行:我拉整个集合只是为了计数,我在客户端进行排序。我如何让SQLAlchemy在数据库端完成这些工作

import operator
vars = {}
for v in db.session.query(Variable).all():
    vars[v.id] = len(v.children)

sorted_vars = sorted(vars.items(), key=operator.itemgetter(1))

获取直系子节点数的一种方法是只按
parent\u id
分组并计数,但正如您所注意到的,您将丢失没有子节点的叶节点。为了解决这个问题,您可以创建计数的子查询,并针对
变量
进行连接,将空值合并为0。另一方面,在这种情况下不需要子查询:

child = aliased(Variable)
session.query(Variable,
              func.count(child.id).label('child_count')).\
    outerjoin(child, Variable.children).\
    group_by(Variable.id).\
    order_by(literal_column('child_count')).\
    all()

由于主键保证不为NULL,因此计数将仅为left生成NULL值,而没有匹配的right,或者没有子项。如果您对实际计数不感兴趣,请将其完全移动到ORDER BY子句中。

既然您似乎只想要直系子女,您就不能按父id分组并按计数排序吗?我喜欢这个主意。问题是,然后我会丢失所有叶节点(它们没有任何子节点,因此不会生成组)。