假设我有这样的一对多关系:
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_books
,books_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中提出了问题。
答案 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]