You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用SQLAlchemy实现复杂对象属性的深度映射?

问题

在SQLAlchemy中是否可以对类属性进行某种‘深度映射’?例如:obj.attr.nested_attr,希望告知obj的映射器如何将nested_attr直接映射到表中的nested_attr列。

环境版本:

  • Python版本:3.10.7
  • SQLAlchemy版本:1.4.41

现有领域模型:

from dataclasses import dataclass
from datetime import datetime

@dataclass
class FullName:
    first_name: str
    last_name: str

@dataclass
class User:
    name: FullName
    date_of_birth: datetime
    # 其他属性...

对应的SQLAlchemy表模型:

import sqlalchemy

metadata = sqlalchemy.MetaData()
users = sqlalchemy.Table(
   "users",
   metadata,
   sqlalchemy.Column("first_name", sqlalchemy.String),
   sqlalchemy.Column("last_name", sqlalchemy.String),
   # 其他列...
)

尝试过以下代码,但SQLAlchemy无法识别点符号的属性路径:

mapper_registry = sqlalchemy.orm.registry()

mapper_registry.map_imperatively(
    User,
    users,
    properties={
        "name.first_name": users.c.first_name,
        "name.last_name": users.c.last_name,
    },
)

请问有没有其他方法可以将user.name.first_name直接映射到users表的first_name列?


解决方案

方法一:使用混合属性同步嵌套属性

为User类添加与表字段对应的混合属性,在属性的get/set方法中自动同步到嵌套的FullName对象,既满足数据库映射需求,又保留领域模型的嵌套结构:

from sqlalchemy.ext.hybrid import hybrid_property
from dataclasses import dataclass
from datetime import datetime
import sqlalchemy
from sqlalchemy.orm import registry

@dataclass
class FullName:
    first_name: str
    last_name: str

@dataclass
class User:
    name: FullName
    date_of_birth: datetime

    @hybrid_property
    def first_name(self):
        return self.name.first_name

    @first_name.setter
    def first_name(self, value):
        self.name.first_name = value

    @hybrid_property
    def last_name(self):
        return self.name.last_name

    @last_name.setter
    def last_name(self, value):
        self.name.last_name = value

# 表定义
metadata = sqlalchemy.MetaData()
users = sqlalchemy.Table(
   "users",
   metadata,
   sqlalchemy.Column("first_name", sqlalchemy.String),
   sqlalchemy.Column("last_name", sqlalchemy.String),
   sqlalchemy.Column("date_of_birth", sqlalchemy.DateTime),
)

# 映射配置
mapper_registry = registry()
mapper_registry.map_imperatively(
    User,
    users,
    properties={
        "first_name": users.c.first_name,
        "last_name": users.c.last_name,
        "date_of_birth": users.c.date_of_birth,
    },
)

方法二:动态添加属性访问逻辑

如果不想修改原User类定义,可以通过动态添加属性的方式,实现嵌套属性与数据库列的映射:

from sqlalchemy.orm import registry
import sqlalchemy

# 原领域模型和表定义保持不变
# ...

# 映射配置
mapper_registry = registry()
mapper_registry.map_imperatively(
    User,
    users,
    properties={
        "first_name": users.c.first_name,
        "last_name": users.c.last_name,
        "date_of_birth": users.c.date_of_birth,
    },
)

# 为User类动态添加嵌套属性的访问逻辑
def _get_name_attr(self, attr_name):
    return getattr(self.name, attr_name)

def _set_name_attr(self, attr_name, value):
    setattr(self.name, attr_name, value)

User.first_name = property(
    lambda self: _get_name_attr(self, "first_name"),
    lambda self, v: _set_name_attr(self, "first_name", v)
)
User.last_name = property(
    lambda self: _get_name_attr(self, "last_name"),
    lambda self, v: _set_name_attr(self, "last_name", v)
)

方法三:使用复合类型(依赖数据库支持)

如果使用PostgreSQL这类支持复合类型的数据库,可以将FullName定义为SQLAlchemy复合类型,直接映射到对应列组合:

from sqlalchemy import CompositeType
from sqlalchemy.orm import registry
import sqlalchemy

# 定义复合类型
full_name_type = CompositeType(
    "full_name",
    [
        sqlalchemy.Column("first_name", sqlalchemy.String),
        sqlalchemy.Column("last_name", sqlalchemy.String),
    ]
)

# 修改表定义,使用复合类型列
metadata = sqlalchemy.MetaData()
users = sqlalchemy.Table(
   "users",
   metadata,
   sqlalchemy.Column("name", full_name_type),
   sqlalchemy.Column("date_of_birth", sqlalchemy.DateTime),
)

# 直接映射User类的name属性
mapper_registry = registry()
mapper_registry.map_imperatively(
    User,
    users,
    properties={
        "name": users.c.name,
        "date_of_birth": users.c.date_of_birth,
    },
)

内容的提问来源于stack exchange,提问作者Maciej G

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 15:30:55