如何在多个Python词典上执行类似INNER JOIN的SQL

时间:2015-03-20 18:36:03

标签: python json django

我目前正在计划一个django应用程序,它允许用户不仅可以构建与模型相关联的自定义表格(例如,用户可以创建一个与“员工”模型相关联的简单自定义“停车位”表,而无需编辑models.py),但也使用这些自定义表构建自定义报告。我能想到的唯一方法是使用一个模型将自定义表数据存储在JSONField中(我使用Postgres作为后端,这实际上效果很好),然后有一个允许用户构建的报表模型并保存“类似SQL”的查询,这些查询返回其自定义报告的已连接数据集。

我已经想出了如何存储自定义表并在我的应用程序中使用它们,我甚至对如何将伪外键上的多个JSON对象合并到自定义报告中有一个宽松的概念,但我只有得到了一对一加入。

使用下面的脚本,如果我的任何dicts在一个外键上有多个记录,则只使用最后一个记录。有没有人知道我如何才能完成多个python词典的一对多连接?

如果我有这三个数据集:

employees = [{"id": 1, "user_id": 303, "name": "Mike"},
             {"id": 2, "user_id": 304, "name": "James"},
             {"id": 3, "user_id": 305, "name": "David"},]

roles = [{"id": 1, "user_id": 303, "role": "Manager"},
         {"id": 2, "user_id": 304, "role": "Assistant"},
         {"id": 3, "user_id": 305, "role": "Assistant"},]

absences = [{"id": 1, "user_id": 303, "date": "2015-03-01"},
            {"id": 2, "user_id": 303, "date": "2015-03-02"},
            {"id": 3, "user_id": 303, "date": "2015-03-03"},
            {"id": 4, "user_id": 304, "date": "2015-03-15"},
            {"id": 5, "user_id": 305, "date": "2015-03-19"},]

我希望直接加入的结果是:

[{'date': '2015-03-01', 'role': 'Manager', 'user_id': 303, 'id': 1, 'name': 'Mike'},
    {'date': '2015-03-02', 'role': 'Manager', 'user_id': 303, 'id': 1, 'name': 'Mike'},
    {'date': '2015-03-03', 'role': 'Manager', 'user_id': 303, 'id': 1, 'name': 'Mike'}, 
    {'date': '2015-03-15', 'role': 'Assistant', 'user_id': 304, 'id': 2, 'name': 'James'}, 
    {'date': '2015-03-19', 'role': 'Assistant', 'user_id': 305, 'id': 3, 'name': 'David'}]

但是由于我的脚本首先遍历我的FROM词典(在本例中是员工),所以我能得到的就是:

[{'date': '2015-03-03', 'role': 'Manager', 'user_id': 303, 'id': 1, 'name': 'Mike'},
{'date': '2015-03-15', 'role': 'Assistant', 'user_id': 304, 'id': 2, 'name': 'James'},
{'date': '2015-03-19', 'role': 'Assistant', 'user_id': 305, 'id': 3, 'name': 'David'}]

以下是我的代码的基础知识:

def joiner(from_table, joins):
    report_data = []

    for row in from_table:
        new_row = row
        for table in joins:
            table_dict = table["table"]
            table_fk = table["fk"]
            for tdr in table_dict:
                if tdr[table_fk] == row[table_fk]:
                    for field in table["fields"]:
                        new_row[field] = tdr[field]
    report_data = from_table
    return report_data

join_tables = [{"table": roles, "fk": "user_id", "fields": ["role"]},
               {"table": absences, "fk": "user_id", "fields": ["date"]},
          ]
joiner(employees, join_tables)

我能想到的最简单的解决方案是从“缺席”字母开始作为from_table而不是员工,但那是一个多对一的连接,这对我的目的非常有限。

此外,如果有人有更好的想法来构建可以使用django在自定义报告中合并的用户创建的数据模式,我会全力以赴。我能想到的唯一其他解决方案是完全绕过django模型,只需使用直接SQL创建,更新和查询所有自定义表。

1 个答案:

答案 0 :(得分:1)

只要您在调用合并时首先列出最长的词典列表(可以轻松修改),这是一个粗略的解决方案

    def merge_lists(listdict1, listdict2,listdict3, joinkey):
        mergedlist=listdict1
        for i in range(len(listdict1)):
            for j in range(len(listdict2)):
                if (listdict1[i][joinkey]==listdict2[j][joinkey]):
                    for keys in listdict2[j].keys():
                        mergedlist[i][keys]=listdict2[j][keys]
            for k in range(len(listdict3)):
                if listdict1[i][joinkey]==listdict3[k][joinkey]:
                    for keys in listdict3[k].keys():
                        mergedlist[i][keys]=listdict3[k][keys]                       
        return mergedlist

    merge_lists(absences, employees, roles,  "user_id")


[  
   {  
      "date":"2015-03-01",
      "id":1,
      "name":"Mike",
      "role":"Manager",
      "user_id":303
   },
   {  
      "date":"2015-03-02",
      "id":1,
      "name":"Mike",
      "role":"Manager",
      "user_id":303
   },
   {  
      "date":"2015-03-03",
      "id":1,
      "name":"Mike",
      "role":"Manager",
      "user_id":303
   },
   {  
      "date":"2015-03-15",
      "id":2,
      "name":"James",
      "role":"Assistant",
      "user_id":304
   },
   {  
      "date":"2015-03-19",
      "id":3,
      "name":"David",
      "role":"Assistant",
      "user_id":305
   }
]