Фильтрация отношений в SQL Alchemy

У меня есть следующий сценарий:

class Author(Base):
  __tablename__ = 'author'

  id    = Column(Integer, primary_key = True)
  name  = Column(String)

  books = relationship('Books', backref='author')


class Book(Base):
  __tablename__ = 'book'

  id    = Column(Integer, primary_key = True)
  title = Column(String)

Что я хотел бы сделать, так это загрузить всех авторов, у которых есть книга, содержащая SQL в названии. то есть

authors = session.query(Author)\
                 .join(Author.books)\
                 .filter(Book.title.like('%SQL%')\
                 .all()

Кажется простым.

Затем я хотел бы перебрать авторов и показать их книги. Я ожидаю, что при доступе к author[0].books будут возвращены ТОЛЬКО книги, в названии которых есть «SQL». Тем не менее, я получаю ВСЕ книги, назначенные этому автору. Фильтр применяется к списку авторов, но не к их книгам, когда я получаю доступ к отношениям.

Как я могу структурировать свой запрос таким образом, чтобы при фильтрации отношений (например, книг) при доступе к этим отношениям фильтрация по-прежнему применялась?


person PCL    schedule 21.02.2014    source источник


Ответы (2)


Прочтите Маршрутизация Явные соединения/утверждения в активно загружаемых коллекциях. Затем с помощью contains_eager вы можете структурировать свой запрос и получить именно то, что вы хотите:

authors = (
        session.query(Author)
        .join(Author.books)
        .options(contains_eager(Author.books)) # tell SA that we load "all" books for Authors
        .filter(Book.title.like('%SQL%'))
    ).all()

Обратите внимание, что на самом деле вы обманываете sqlalchemy, заставляя его думать, что он загрузил всю коллекцию Author.books, и поэтому ваш сеанс будет знать false информацию о реальном состоянии мира.

person van    schedule 21.02.2014

Короче говоря, это невозможно. (Если бы это было так, экземпляр Author имел бы другой атрибут books в зависимости от того, как он был запрошен, что не имеет смысла.)

Вместо этого вы можете запросить обратное отношение:

books = session.query(Book) \
               .filter(Book.title.like('%SQL%')) \
               .all()

Затем вы можете получить доступ к .author для каждой книги, чтобы собрать вместе книги, написанные одним и тем же автором.

person univerio    schedule 21.02.2014