SQLAlchemy查询将相关数据的伪列添加为dict数组

问题描述

以下内容是有关使用python编写的Web应用程序后端,并使用sqlAlchemy连接到Postgres(v9.5)关系数据库

假设我们有两个表:关系。每个项目都有一个ID,并且可能与多个其他项目相关。此类关系会保留在关系表中,该表存储自己的ID,两个相关项目的ID以及关系的名称

以下是一些示例数据:

     items:                               relations:

  id      name                id     item1_id  item2_id    name
---------------              ----------------------------------
  1       car                  1        1         2       weight
  2       boat                 2        1         3       colour
  3       bike                 3        1         4        age
  4       jet-pack             4        5         1        age
  5       plane
  6       rocket          

因此,例如 car bike 通过颜色关联。

我正在寻找数据库查询sqlAlchemy代码提取,以返回所有以及附加的派生-伪列包含每个元组的关系数据作为结构数组:

 id       name                                 relations
---------------------------------------------------------------------------------
  1       car            [ { 2,weight },{ 3,colour },{ 4,age },{ 5,age } ]
  2       boat           [ { 1,weight } ]
  3       bike           [ { 1,colour } ]
  4       jet-pack       [ { 1,age } ]
  5       plane          [ { 1,age } ]
  6       rocket         []

一个好的第一步是在尝试转换为sqlAlchemy之前生成SQL查询。与我已经达到的最接近的结果:

SELECT i.*,(SELECT array_agg(row(CASE WHEN r.item1_id=i.id THEN r.item2_id ELSE r.item1_id END,i.name)) 
   AS relations from relations r 
   WHERE r.item1_id=i.id OR r.item2_id=i.id) 
FROM items i ;

但是更好的一点是将关系实际输出为json:

SELECT i.*,(SELECT jsonb_agg(jsonb_build_object('item',CASE WHEN r.item1_id=i.id THEN r.item2_id ELSE r.item1_id END,'relation',r.name))     
     AS relations
     FROM relations r   
     WHERE r.item1_id=i.id OR r.item2_id=i.id)
  FROM items i;

产生:

 id       name                                 relations
---------------------------------------------------------------------------------
  1       car            [ { "item": 2,"relation": "weight" },{ "item": 3,"relation": "colour" },{ "item": 4,"relation": "age" },{ "item": 5,"relation": "age" } ]
  2       boat           [ { "item": 1,"relation": "weight" } ]
  3       bike           [ { "item": 1,"relation": "colour" } ]
  4       jet-pack       [ { "item": 1,"relation": "age" } ]
  5       plane          [ { "item": 1,"relation": "age" } ]
  6       rocket         []

如何在sqlalchemy中实现此子查询

解决方法

在浏览了SQLalchemy文档和许多网站之后,我在这里找到了基于相关子查询的解决方案:

Can we make correlated queries with SQLAlchemy-答案1。

对此进行如下调整即可得到我一直在寻找的东西:

from sqlalchemy import select,func,table,Column,Integer,Text,case
from sqlalchemy.sql.expression import or_


items     = table('items',Column('id',Integer),Column('name',Text))

relations = table('relations',Column('item1_id',Column('item2_id',Text))


subquery = select( [ func.jsonb_agg(
                       func.jsonb_build_object(
                         'item',case( [ ( relations.c.item1_id == items.c.id,relations.c.item2_id ) ],else_ = relations.c.item1_id),'relation',relations.c.name
                       )
                     )
                   ]
                 ).where(or_(relations.c.item1_id == items.c.id,relations.c.item2_id == items.c.id) \
                 .correlate(items)

query = (
    select([items.*,subquery.label('relations')]).select_from(items)
)