SQLAlchemy/Elixir - 查询实体在多对多关系中的成员资格
我正在尝试构建一个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))]
第一步创建了一个助手的列表列表。第二步将这个列表列表扁平化,并通过创建一个集合来去除重复项。