SQLAlchemy/Elixir - 查询实体在多对多关系中的成员资格

1 投票
1 回答
882 浏览
提问于 2025-04-16 19:22

我正在尝试构建一个sqlalchemy查询,以获取所有在MIT担任助理教授的教授的名字列表。需要注意的是,某个课程可能会有多个助理教授。

我想做的事情大致相当于:

uni_mit = University.get_by(name='MIT')
s = select([Professor.name],
           and_(Professor.in_(Course.assistants),
                Course.university = uni_mit))
session.execute(s)

这样做是行不通的,因为in_只适用于实体的字段,而不是整个实体。不能使用Professor.id.in_,因为Course.assistants是一个教授的列表,而不是他们的ID列表。我也试过contains,但也没有成功。

我的Elixir模型是:

class Course(Entity):
    id = Field(Integer, primary_key=True)
    assistants = ManyToMany('Professor', inverse='courses_assisted', ondelete='cascade')
    university = ManyToOne('University')
    ..

class Professor(Entity):
    id = Field(Integer, primary_key=True)
    name = Field(String(50), required=True)
    courses_assisted = ManyToMany('Course', inverse='assistants', ondelete='cascade')
    ..

如果我能访问中间的多对多实体,那就简单多了(条件会是and_(interm_table.prof_id = Professor.id, interm_table.course = Course.id)),但SQLAlchemy显然把这个表隐藏了。

我使用的是Elixir 0.7和SQLAlchemy 0.6。

顺便说一下:这个问题和Sqlalchemy+elixir: 如何查询多对多关系?不同,因为我需要检查教授是否符合所有满足条件的课程,而不是一个单一的、静态的课程。

1 个回答

0

你可以找到Elixir隐藏的中间表,但要注意,它使用的是完全限定的列名(比如 __package_path_with_underscores__course_id)。为了避免这种情况,可以像下面这样定义你的多对多关系:

class Course(Entity):
    ...
    assistants = ManyToMany('Professor', inverse='courses_assisted',
                            local_colname='course_id', remote_colname='prof_id',
                            ondelete='cascade')

然后你就可以通过下面的方式访问中间表:

rel = Course._descriptor.find_relationship('assistants')
assert rel
table = rel.table

并且可以通过 table.c.prof_id 等方式访问列。

更新:当然,你可以在更高的层次上做到这一点,但不能在一个查询中完成,因为SQLAlchemy还不支持在关系中使用 in_。例如,可以通过两个查询来实现:

>>> mit_courses = set(Course.query.join(
... University).filter(University.name == 'MIT'))
>>> [p.name for p in Professor.query if set(
... p.courses_assisted).intersection(mit_courses)]

或者,另外一种方式:

>>> plist = [c.assistants for c in Course.query.join(
... University).filter(University.name == 'MIT')]
>>> [p.name for p in set(itertools.chain(*plist))]

第一步创建了一个助手的列表列表。第二步将这个列表列表扁平化,并通过创建一个集合来去除重复项。

撰写回答