首页 > 解决方案 > python sqlalchemy orm 一对多双向关系 - 将json映射到多个表

问题描述

我正在使用 sqlalchemy orm 将 json 映射到多个表。我有两张桌子,每张桌子都有一个班级。

其中一个表有一个参数,它是一个字典列表,必须映射到第二个表的不同列。


from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship

Base = declarative_base()

class A(Base):
    __tablename__ = 'a'
    id = Column(primary_key, Interger(10))
    param1 = Column(String(45))
    param2 = Column(String(45))
    atobmap = relationship('B', lazy='subquery', back_populates="a")

class B(Base):
    __tablename__ = 'b'
    bid = Column(primary_key=True, nullable=True, Integer(15))
    bparam1 = Column(ForeignKey('a.id'), nullable=False)
    bparam2 = Column(String(45))
    a = relationship('A', back_populates='b')

示例 json:

{
    "id": 1,
    "param1": "Value",
    "param2": "Value",
    "atobmap": [
                 { "bid": 1,
                   "bparam1": 1,
                   "bparam2": "Value"
                 },
                 { "bid": 2,
                   "bparam1": 1,
                   "bparam2": "Value"
                 }
               ]
}

当我手动将数据插入表并执行 GET 时,它工作正常并且按预期返回值,尽管它在我的 atobmap 字典中添加了一个带有 value {} 的 _sa_instance_state 键。

{
...
"atobmap": [
                 { "_sa_instance_state": {}
                   "bid": 1,
                   "bparam1": 1,
                   "bparam2": "Value"
                 },
                 { "_sa_instance_state": {}
                   "bid": 2,
                   "bparam1": 1,
                   "bparam2": "Value"
                 }
       ]
}            

但是当我尝试发布值时,它会抛出一个错误"Unhashable type: dict".

标签: pythonsqlalchemy

解决方案


您可以使用类似于下面的功能。

它接受一个 SQLAlchemy 模型和一个字段/值对字典并返回一个模型实例。忽略字典中不是模型字段的键。遍历和实例化关系。

例子:

我们的模型包括一个 Company 模型,其 name 字段是字符串,users 字段是与 User 模型的一对多关系。

我们的数据如下所示。

{“公司”:{“名称”:“Owoga”“用户”:[{“名称”:“埃里克”}]}}

我们将使用 SQLAlchemy 的class_mapper函数来获取模型的“映射器”。然后我们可以访问mapper.attrs.keys()以获取所有映射字段的列表。任何不是关系的键,只需将它们从数据中提取出来。对于作为关系的键,获取关系的映射器,然后from_json使用该相关映射器和子数据递归调用。然后,将该新实例附加到父实例。

def from_json(model, data):
    mapper = class_mapper(model)
    keys = mapper.attrs.keys()
    relationships = inspect(mapper).relationships
    args = {k: v for k, v in data.items() if k in keys and k not in relationships}
    instance = model(**args)
    for attr, relationship in relationships.items():
        if relationship.direction in [MANYTOMANY, ONETOMANY]:
            relationship_instances = [
                from_json(relationship.mapper.class_, el) for el in data[attr]
            ]
            setattr(instance, attr, relationship_instances)
        elif relationship.direction == MANYTOONE:
            try:
                relationship_instance = from_json(
                    relationship.mapper.class_, data[attr]
                )
            except KeyError:
                pass
            else:
                setattr(instance, attr, relationship_instance)
    return instance

推荐阅读