Я пытаюсь построить запрос 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 - это список профессоров, а не список их идентификаторов, Я также пробовал 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')
..
Это было бы тривиально, если бы я мог получить доступ к промежуточному объекту many- to- (условие было бы and_(interm_table.prof_id = Professor.id, interm_table.course = Course.id)
, но SQLAlchemy, по-видимому, скрывает эту таблицу от меня.
Я использую Elixir 0.7 и SQLAlchemy 0.6.
Btw: этот вопрос отличается от Sqlalchemy+ elixir: как запрос с отношениями ManyToMany?, в котором мне нужно проверить профессоров на всех курсах, которые удовлетворяют условие, а не одно, статическое.
Вы можете найти промежуточную таблицу, в которой Elixir скрыл ее, но обратите внимание, что она использует полные имена столбцов (например, __package_path_with_underscores__course_id
). Чтобы этого избежать, определите свой ManyToMany, например,
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))]
На первом этапе создается список списков помощников. Второй шаг выравнивает список списков и удаляет дубликаты посредством создания набора.
contains
/ in_
я не знаю)? В конце концов, ORM предназначены для абстрагирования от способа хранения данных.