如何使用SQLAlchemy为嵌套(数据)类创建自定义JSONMap(2)

w46czmvw  于 2023-05-02  发布在  其他
关注(0)|答案(1)|浏览(120)

我想持久化一个python(data)类(i.例如Person)。这个类的一个字段引用另一个类(City)。第二个类只是两个dicts的 Package 器,我想以非规范化的方式将其存储为JSON列。
我的示例类看起来像这样:

@dataclass
class Person:
  name: str
  city: City

@dataclass
class City:
 property_a: dict
 property_b: dict

在数据库中,它应该看起来像这样:

+--------+-----------------------------------------------------------------+
|  name  |                              city                               |
+--------+-----------------------------------------------------------------+
| aaron  | {property_a: {some_value: 1}, property_b: {another_value: 2}}   |
| bob    | {property_a: {some_value: 10}, property_b: {another_value: 20}} |
+--------+-----------------------------------------------------------------+

我的表定义如下所示:

person_table = Table(
  "persons",
   Column("id", Integer, primary_key=True, autoincrement=True),
   Column("name", String),
   Column("city", JSON)
)

mapper_registry.map_imperatively(
  Person,
  person_table
)

这显然失败了,因为“城市类型的对象不是JSON可序列化的”。我需要为mapper_registry提供自定义(反)序列化方法,该方法告诉SqlAlchemy如何将我的City类转换为嵌套的dict。但我找不到如何做到这一点(即使这是一个很好的方法)。

sycxhyv7

sycxhyv71#

我找到解决办法了这是否是一个优雅的方式来解决这个问题或不是由读者来决定。我最初的理由是不要用I/O实现细节污染模型。另一种方法是引入服务层和/或创建额外的dumb DTO,这些DTO可以转换为/形成模型类,并包含city字段作为普通的dict/json。
需要两个额外的函数来引入custon de-/serialization:

def serialize_json(o):
  match o:
    case City():
       return json.dumps({"property_a": o.property_a, "property_b": o.property_b})
    case _:
      return json.dumps(o)

def deserialize_json(s):
  s_dict = json.loads(s)
  match s_dict:
    case {"property_a": property_a, "property_b": property_b}:
      return City(property_a=property_a, property_b=propertyb)
    case _:
      return s_dict

match操作符对于这个简单的例子来说并不是绝对必要的,但是如果需要更多的例子,它可以使所有的东西都更干净。
现在可以将函数传递给SQLAlchemy:

from sqlalchemy import create_engine
engine = create_engine(
  "sqlite:///:memory:", 
  json_serializer=orm.serialize_json, 
  json_deserializer=orm.deserialize_json
)

相关问题