SQLAlchemy / Elixir - запрос проверки членства объекта в списке отношений «многие ко многим»

1

Я пытаюсь построить запрос 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?, в котором мне нужно проверить профессоров на всех курсах, которые удовлетворяют условие, а не одно, статическое.

  • 0
    Это не sqlalchemy, который скрывает вторичную таблицу; в ванильной sqlalchemy вы должны назвать и описать эту таблицу, но не рекомендуется сопоставлять ее с классом. Обфускация происходит за счет эликсира.
  • 0
    Я обновил свой ответ в ответ на ваш комментарий.
Теги:
many-to-many
sqlalchemy
python-elixir

1 ответ

0
Лучший ответ

Вы можете найти промежуточную таблицу, в которой 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))]

На первом этапе создается список списков помощников. Второй шаг выравнивает список списков и удаляет дубликаты посредством создания набора.

  • 0
    Спасибо! При этом решение будет простым, но довольно длинным и многословным. Есть ли способ решения этой проблемы без доступа к промежуточной таблице (например, какой-то синтаксис contains / in_ я не знаю)? В конце концов, ORM предназначены для абстрагирования от способа хранения данных.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню