sqlalchemy orm | fastAPI查询+连接三个表得到parents和所有children
sqlalchemy orm | fastAPI querying + joining three tables to get parents and all children
我试图在我的 fastAPI 中创建一个路由,该路由返回所有 parents.portfolios
和所有 children 或 stocks
的列表与它们中的每一个相关联加上关联中的额外数据table(对于该关系)。
响应应该看起来有点像这样
[ { "parrent1_attr1": bla,
"parrent1_attr2": bla,
"children": [ {
"child1_attr1": bla,
"child1_attr2": bla},
{"child2_attr1": bla,
"child2_attr2": bla}]
},
etc...]
现在产生这个的路线看起来像这样:
@router.get("/")
def get_all_portfolios(db: Session = Depends(get_db), current_user: int = Depends(oauth2.get_current_user)):
results = db.query(models.Portfolio).options(joinedload(models.Portfolio.stocks)).all()
return results
但这给了我错误的结果。
这就是结果。
[ { "parrent1_attr1": bla,
"parrent1_attr2": bla,
"children": [ {
"association_table_attr1": bla
"association_table_attr2": bla},]
所以我从关联 table 中获取数据,而不是从 children 中获取数据。
我手头的模型都在这里
class Portfolio(Base):
__tablename__ = "portfolios"
id = Column(Integer, primary_key=True, nullable=False)
...
stocks = relationship("PortfolioStock", back_populates="portfolio")
class Stock(Base):
__tablename__ = "stocks"
id = Column(Integer, primary_key=True, nullable=False)
...
portfolios = relationship("PortfolioStock", back_populates="stock")
class PortfolioStock(Base):
__tablename__ = "portfolio_stocks"
id = Column(Integer, primary_key=True)
stock_id = Column(Integer, ForeignKey("stocks.id", ondelete="CASCADE"))
portfolio_id = Column(Integer, ForeignKey("portfolios.id", ondelete="CASCADE"))
count = Column(Integer, nullable=True)
buy_in = Column(Float, nullable=True)
stock = relationship("Stock", back_populates="portfolios")
portfolio = relationship("Portfolio", back_populates="stocks")
如果您需要更多信息,请告诉我。感谢您的帮助。
我发现给协会起一个自己的名字会更容易,因为这很容易混淆,但在这种情况下 Portfolio.stocks
实际上是协会对象的列表,而不是实际的股票。您必须将它们从关联对象中移除。在我下面的示例中,我去 assoc.stock.id
购买股票。那不应该触发另一个查询,因为我们使用 joinedload 来 pre-load 它。如果股票有名称,我们会用 assoc.stock.name
.
来引用它
with Session(engine) as session:
q = session.query(Portfolio).options(joinedload(Portfolio.stocks).joinedload(PortfolioStock.stock))
for portfolio in q.all():
print (f"Listing associated stocks for portfolio {portfolio.id}")
for assoc in portfolio.stocks:
print (f" Buy in {assoc.buy_in}, count {assoc.count} and stock id {assoc.stock.id}")
查询看起来像这样:
SELECT portfolios.id AS portfolios_id, stocks_1.id AS stocks_1_id, portfolio_stocks_1.id AS portfolio_stocks_1_id, portfolio_stocks_1.stock_id AS portfolio_stocks_1_stock_id, portfolio_stocks_1.portfolio_id AS portfolio_stocks_1_portfolio_id, portfolio_stocks_1.count AS portfolio_stocks_1_count, portfolio_stocks_1.buy_in AS portfolio_stocks_1_buy_in
FROM portfolios LEFT OUTER JOIN portfolio_stocks AS portfolio_stocks_1 ON portfolios.id = portfolio_stocks_1.portfolio_id LEFT OUTER JOIN stocks AS stocks_1 ON stocks_1.id = portfolio_stocks_1.stock_id
对于任何正在寻找答案的人,这是我修复它的方法。
我使用了上面提到的 Ian 的查询(非常感谢)。
然后我只是手动声明了我想要的结构。
整个代码如下所示
results = (
db.query(models.Portfolio)
.options(joinedload(models.Portfolio.stocks).joinedload(models.PortfolioStock.stock))
.all()
)
result_list = []
for portfolio in results:
result_dict = portfolio.__dict__
stock_list = []
for sto in result_dict["stocks"]:
sto_dict = sto.__dict__
temp_sto = {}
temp_sto = sto_dict["stock"]
setattr(temp_sto, "buy_in", sto_dict["buy_in"])
setattr(temp_sto, "count", sto_dict["count"])
stock_list.append(temp_sto)
result_dict["stocks"] = stock_list
result_list.append(result_dict)
return result_list
我在这里所做的是首先声明一个空列表,其中将存储我们的最终结果,并将 returned。
然后我们遍历查询(因为查询给了我们一个列表)。
所以我们现在有每个“SQL 炼金术模型”作为 portfolio
。
然后我们可以将其转换为字典并使用 result_dict = portfolio.__dict__
为其分配一个新变量 __dict__
方法将模型转换为您可以轻松使用的 Python 字典。
因为 result_dict
包含 PortfolioStock models
的列表,它是关联 table 模型。这些存储在 stocks
键中,我们必须迭代它们也获得这些价值。而这里我们只是重复这个过程。
我们使用 __dict__
将模型转换为字典,然后制作一个新的空字典 temp_sto={}
并将其设置为等于 stock
键,该键是链接 child 加入我们的协会 table。所以现在我们有了要访问的 child 或 stock
。我们可以简单地将新的空字典设置为等于那个,这样我们就可以继承其中包含的所有信息。
然后我们只需要添加我们可能想要的关联 table 中的所有其他信息,这些信息可以通过我们在 for 循环 sto_dict
.
开头定义的字典访问
一旦我们有了这个,我们就将它附加到我们在这个 for 循环之外但在 portfolio loop
内定义的空列表中。
将 result_dict["stocks"]
键(所以基本上是你希望所有 children 包含在其中的键)等于我们刚刚将所有字典附加到的列表,然后将该字典附加到我们的 result_list
.
最后要做的就是 return 该列表,我们就完成了。
我在下面提供了一种不可知论的方法
query = db.query(Parent).options(joinedload(Parent.relationship).joinedload(AssociationTable.child).all()
result_list = []
for parent in query:
parent_dict = parent.__dict__
child_list = []
for association in parent_dict["relationship_name"]:
association_dict = association.__dict__
temp_child_dict = {}
temp_child_dict = association_dict["child"]
setattr(temp_child_dict, "name_you_want", association_dict["key_of_value_you_want"])
# repeat as many times as you need
child_list.append(temp_child_dict)
parent_dict["children"] = child_list
result_list.append(parent_dict)
return result_list
希望对遇到类似情况的你有所帮助。
我试图在我的 fastAPI 中创建一个路由,该路由返回所有 parents.portfolios
和所有 children 或 stocks
的列表与它们中的每一个相关联加上关联中的额外数据table(对于该关系)。
响应应该看起来有点像这样
[ { "parrent1_attr1": bla,
"parrent1_attr2": bla,
"children": [ {
"child1_attr1": bla,
"child1_attr2": bla},
{"child2_attr1": bla,
"child2_attr2": bla}]
},
etc...]
现在产生这个的路线看起来像这样:
@router.get("/")
def get_all_portfolios(db: Session = Depends(get_db), current_user: int = Depends(oauth2.get_current_user)):
results = db.query(models.Portfolio).options(joinedload(models.Portfolio.stocks)).all()
return results
但这给了我错误的结果。
这就是结果。
[ { "parrent1_attr1": bla,
"parrent1_attr2": bla,
"children": [ {
"association_table_attr1": bla
"association_table_attr2": bla},]
所以我从关联 table 中获取数据,而不是从 children 中获取数据。
我手头的模型都在这里
class Portfolio(Base):
__tablename__ = "portfolios"
id = Column(Integer, primary_key=True, nullable=False)
...
stocks = relationship("PortfolioStock", back_populates="portfolio")
class Stock(Base):
__tablename__ = "stocks"
id = Column(Integer, primary_key=True, nullable=False)
...
portfolios = relationship("PortfolioStock", back_populates="stock")
class PortfolioStock(Base):
__tablename__ = "portfolio_stocks"
id = Column(Integer, primary_key=True)
stock_id = Column(Integer, ForeignKey("stocks.id", ondelete="CASCADE"))
portfolio_id = Column(Integer, ForeignKey("portfolios.id", ondelete="CASCADE"))
count = Column(Integer, nullable=True)
buy_in = Column(Float, nullable=True)
stock = relationship("Stock", back_populates="portfolios")
portfolio = relationship("Portfolio", back_populates="stocks")
如果您需要更多信息,请告诉我。感谢您的帮助。
我发现给协会起一个自己的名字会更容易,因为这很容易混淆,但在这种情况下 Portfolio.stocks
实际上是协会对象的列表,而不是实际的股票。您必须将它们从关联对象中移除。在我下面的示例中,我去 assoc.stock.id
购买股票。那不应该触发另一个查询,因为我们使用 joinedload 来 pre-load 它。如果股票有名称,我们会用 assoc.stock.name
.
with Session(engine) as session:
q = session.query(Portfolio).options(joinedload(Portfolio.stocks).joinedload(PortfolioStock.stock))
for portfolio in q.all():
print (f"Listing associated stocks for portfolio {portfolio.id}")
for assoc in portfolio.stocks:
print (f" Buy in {assoc.buy_in}, count {assoc.count} and stock id {assoc.stock.id}")
查询看起来像这样:
SELECT portfolios.id AS portfolios_id, stocks_1.id AS stocks_1_id, portfolio_stocks_1.id AS portfolio_stocks_1_id, portfolio_stocks_1.stock_id AS portfolio_stocks_1_stock_id, portfolio_stocks_1.portfolio_id AS portfolio_stocks_1_portfolio_id, portfolio_stocks_1.count AS portfolio_stocks_1_count, portfolio_stocks_1.buy_in AS portfolio_stocks_1_buy_in
FROM portfolios LEFT OUTER JOIN portfolio_stocks AS portfolio_stocks_1 ON portfolios.id = portfolio_stocks_1.portfolio_id LEFT OUTER JOIN stocks AS stocks_1 ON stocks_1.id = portfolio_stocks_1.stock_id
对于任何正在寻找答案的人,这是我修复它的方法。
我使用了上面提到的 Ian 的查询(非常感谢)。 然后我只是手动声明了我想要的结构。
整个代码如下所示
results = (
db.query(models.Portfolio)
.options(joinedload(models.Portfolio.stocks).joinedload(models.PortfolioStock.stock))
.all()
)
result_list = []
for portfolio in results:
result_dict = portfolio.__dict__
stock_list = []
for sto in result_dict["stocks"]:
sto_dict = sto.__dict__
temp_sto = {}
temp_sto = sto_dict["stock"]
setattr(temp_sto, "buy_in", sto_dict["buy_in"])
setattr(temp_sto, "count", sto_dict["count"])
stock_list.append(temp_sto)
result_dict["stocks"] = stock_list
result_list.append(result_dict)
return result_list
我在这里所做的是首先声明一个空列表,其中将存储我们的最终结果,并将 returned。
然后我们遍历查询(因为查询给了我们一个列表)。
所以我们现在有每个“SQL 炼金术模型”作为 portfolio
。
然后我们可以将其转换为字典并使用 result_dict = portfolio.__dict__
为其分配一个新变量 __dict__
方法将模型转换为您可以轻松使用的 Python 字典。
因为 result_dict
包含 PortfolioStock models
的列表,它是关联 table 模型。这些存储在 stocks
键中,我们必须迭代它们也获得这些价值。而这里我们只是重复这个过程。
我们使用 __dict__
将模型转换为字典,然后制作一个新的空字典 temp_sto={}
并将其设置为等于 stock
键,该键是链接 child 加入我们的协会 table。所以现在我们有了要访问的 child 或 stock
。我们可以简单地将新的空字典设置为等于那个,这样我们就可以继承其中包含的所有信息。
然后我们只需要添加我们可能想要的关联 table 中的所有其他信息,这些信息可以通过我们在 for 循环 sto_dict
.
一旦我们有了这个,我们就将它附加到我们在这个 for 循环之外但在 portfolio loop
内定义的空列表中。
将 result_dict["stocks"]
键(所以基本上是你希望所有 children 包含在其中的键)等于我们刚刚将所有字典附加到的列表,然后将该字典附加到我们的 result_list
.
最后要做的就是 return 该列表,我们就完成了。
我在下面提供了一种不可知论的方法
query = db.query(Parent).options(joinedload(Parent.relationship).joinedload(AssociationTable.child).all()
result_list = []
for parent in query:
parent_dict = parent.__dict__
child_list = []
for association in parent_dict["relationship_name"]:
association_dict = association.__dict__
temp_child_dict = {}
temp_child_dict = association_dict["child"]
setattr(temp_child_dict, "name_you_want", association_dict["key_of_value_you_want"])
# repeat as many times as you need
child_list.append(temp_child_dict)
parent_dict["children"] = child_list
result_list.append(parent_dict)
return result_list
希望对遇到类似情况的你有所帮助。