SQLAlchemy:如何通过对象的PK从关系中获取对象?

时间:2012-08-20 03:06:19

标签: python sqlalchemy

假设我有这样的一对多关系:

class Book(Base):
    __tablename__ = "books"
    id = Column(Integer)
    ...
    library_id = Column(Integer, ForeignKey("libraries.id"))

class Library(Base):
    __tablename__ = "books"
    id = Column(Integer)
    ...
    books = relationship(Book, backref="library")

现在,如果我有一本书的ID,有没有办法从Library.books关系中检索它,“在这个特定的库中找到一本id = 10的书”?类似的东西:

try:
    the_book = some_library.books.by_primary_key(10) 
except SomeException:
    print "The book with id 10 is not found in this particular library"

我能想到的解决方法(但我宁愿避免使用):

book = session.query(Book).get(10)
if book and book.library_id != library_id:
    raise SomeException("The book with id 10 is not found in this particular library")

book = session.query(Book).filter(Book.id==10).filter(Book.library_id=library.id).one()

原因:想象有几种不同的关系(scifi_booksbooks_on_loan等),它们指定了不同的主要条件 - 手动查询需要为所有这些条件编写单独的查询,而SQLAlchemy 已经知道如何检索该关系的项目。此外,我更喜欢一次性加载书籍(通过访问library.books),而不是发出单独的查询。

另一个有效但效率低下的选择是:

for b in library.books:
    if b.id == book_id:
        return b

我目前使用的是:

library_books = {b.id:b for b in library.books}
for data in list_of_dicts_containing_book_id:
    if data['id'] in library_books:
        library_books[data['id']].do_something(data)
    else:
        print "Book %s is not in the library" % data['id']

我希望有一种更好的内置方式可以快速从关系中检索项目

UPD:我在sqlalchemy mail list中提出了问题。

2 个答案:

答案 0 :(得分:2)

请参阅querying with joins上的SQLAlchemy文档。所以你想要这样的东西(要注意这是未经测试的):

query(Book, Library). \
    filter(Book.id==10). \
    filter(Book.library.id==needed_library_id).all()

如果Book -> Library引用是标量,则可以使用has()

query.filter(Library.books.has(id=10))

要一次批量查询多本图书,您可以使用in_()运算符:

query(Library).join('books', Book).filter(Book.id.in_([1, 2, 10])).all()

答案 1 :(得分:2)

SQLAlchemy的查询对象具有with_parent方法,它正是这样做的:

  

with_parent(instance,property = None)

     

使用其属性状态以及已建立的relationship()配置,添加将给定实例与子对象或集合相关联的过滤条件。

所以在我的例子中,代码看起来像

q = session.query(Book)
q = q.with_parent(my_library, "scifi_books")
q = q.filter(Book.id==10).one()

即使已加载my_library.scifi_books关系,也会发出单独的查询。似乎没有“内置”方法通过PK从已经加载的关系中检索项目,所以最简单的方法是将关系转换为dict并使用它来查找项目:

book_lookup = {b.id: b for b in my_library.scifi_books}
book = books_lookup[10]