数据分析agent(十三_2):业务层和持久层相互转换mapper

二,属性 解释

python 复制代码
role:Mapped[str]=mapped_column(
        String(32),
        comment="列类别 (primary_key,forergn_key,measure,dimension)"
    )
 

role: Mapped[str](Python 类型注解)
Mapped 是 SQLAlchemy 提供的一个泛型类型,用于在声明式模型中标注该属性在 Python 中的数据类型。
Mapped[str] 表示这个字段在 Python 层面是一个字符串类型。SQLAlchemy 会根据这个类型提示,自动推断出对应的数据库数据类型(如果不显式指定的话)以及该列是否允许为 NULL。

mapped_column(...)(数据库列配置)
这个函数用于描述该列在数据库中的具体约束和规格。

String(32):显式指定了数据库中的数据类型为 VARCHAR,且最大长度限制为 32 个字符。如果这里不传参数,SQLAlchemy 会默认使用 Mapped[str] 推导出的类型,但显式指定长度是更严谨的做法。
comment="列类别...":为该数据库字段添加注释说明。这在生成数据库 DDL(如 CREATE TABLE 语句)时会作为列注释写入数据库,有助于后续维护时理解该字段的业务含义。

primary_key (主键):
代表该列是表的主键,用于唯一标识表中的每一行记录。

forergn_key (外键):
注:这里存在一个拼写错误,正确的英文应该是 foreign_key。
代表该列是外键,用于关联其他表,建立表与表之间的关系。

measure (度量):
在数据仓库中,度量通常指可被聚合计算的数值型数据。例如:销售额、利润、订单数量、温度等。它们通常用于图表中的 Y 轴或指标卡片。

dimension (维度):
维度是指用于观察和分析数据的上下文或分类属性。例如:时间(年/月/日)、地区、产品类别、客户名称等。它们通常用于图表中的 X 轴、分组(Group By)或筛选器。

三,各层的数据例子 column 列 来举例:

1,业务层,和应用层web,小程序 等交互的,column_info.py:

python 复制代码
# 列对象 的类,业务层,用来表示 各个列 的对象 的封装

# 8给 个属性
from dataclasses import dataclass
from typing import Any

@dataclass

class ColumnInfo:
    id:str
    name:str
    type:str # 列 数据类型

    role:str
    # 列类别 (
    # primary_key 主键,forergn_key 外键 ,measure 度量:数值,dimension 维度:维度表中的属性

    examples:list[Any]
    # 数据示例
    description:str

    alias:list[str]

    table_id:str

2,持久层,和数据库交互的 column_info_mysql.py

python 复制代码
from sqlalchemy.orm import Mapped, mapped_column

from day05_Info_model.models.base import Base

from sqlalchemy import String,Text

from  sqlalchemy.types import JSON
class ColumnInfoMySQL(Base):

    __tablename__ = "coloumn_info"

    id:Mapped[str]=mapped_column(
        String(64),
        primary_key=True,

        comment="列的编号"

    )

    name:Mapped[str |None]=mapped_column(
        String(128),
        comment="列名称"
    )

    type:Mapped[str | None]=mapped_column(
        String(64),
        comment="列 数据类型"
    )

    role:Mapped[str | None]=mapped_column(
        String(32),
        comment="列类别 (primary_key 主键,forergn_key 外键 ,measure 度量:数值,dimension 维度:维度表中的属性 )"
    )

    examples:Mapped[dict| list | None]=mapped_column(
        JSON,
        comment="列对象的 举例说明,数据示例"
    )

    description:Mapped[str |None]=mapped_column(
        Text,
        comment="列 的 描述 说明,是做什么的"
    )

    alias:Mapped[dict | list | None]=mapped_column(
        JSON,
        comment="列别名"
    )

    table_id:Mapped[str | None]=mapped_column(
        String(64),
        comment="列 所在的 对应的表 的编号 id"
    )

3,业务层和持久层的相互转换,column_info_mapper.py:

python 复制代码
from dataclasses import asdict

from day05_Info_model.entities.column_info import ColumnInfo
from day05_Info_model.models.coloumn_info_mysql import ColumnInfoMySQL


class ColumnInfoMapper:

    @staticmethod
    def to_entity(column_info_mysql:ColumnInfoMySQL)->ColumnInfo:

        return ColumnInfo(
            id=column_info_mysql.id,
            name=column_info_mysql.name,
            type=column_info_mysql.type,
            role=column_info_mysql.role,

            examples=column_info_mysql.examples,
            description=column_info_mysql.description,
            alias=column_info_mysql.alias,
            table_id=column_info_mysql.table_id
        )

    @staticmethod
    def to_model(column_info:ColumnInfo)->ColumnInfoMySQL:

        return ColumnInfoMySQL(**asdict(column_info))

四,mapper:业务层和持久层转换 验证测试:

1,1_column_mapper_test.py:

python 复制代码
# main_test.py
from dataclasses import asdict
from day05_Info_model.entities.column_info import ColumnInfo
from day05_Info_model.models.coloumn_info_mysql import ColumnInfoMySQL
from day05_Info_model.mappers.column_info_mapper import ColumnInfoMapper

# 1. 构造业务层对象 (Entity)
entity = ColumnInfo(
    id="col_001",
    name="sales_amount",
    type="DECIMAL(10,2)",
    role="measure",
    examples=[199.99, 250.00],
    description="销售总金额",
    alias=["销售额", "营收"],
    table_id="tbl_order"
)

print("=== 1. 原始业务实体 (Entity) ===")
print(entity)  # 触发 dataclass 的 __repr__
print()

# 2. Entity -> Model (业务层转持久层)
model = ColumnInfoMapper.to_model(entity)

print("=== 2. 转换后的数据库模型 (Model) ===")
print(model)   # 触发 SQLAlchemy 声明式对象的 __repr__
print()

# 3. Model -> Entity (持久层转回业务层)
new_entity = ColumnInfoMapper.to_entity(model)

print("=== 3. 转换回业务实体 (Entity) ===")
print(new_entity)

'''
=== 1. 原始业务实体 (Entity) ===
ColumnInfo(id='col_001', name='sales_amount', type='DECIMAL(10,2)', role='measure', examples=[199.99, 250.0], description='销售总金额', alias=['销售额', '营收'], table_id='tbl_order')

=== 2. 转换后的数据库模型 (Model) ===
<day05_Info_model.models.coloumn_info_mysql.ColumnInfoMySQL object at 0x00000210DB15AD50>

=== 3. 转换回业务实体 (Entity) ===
ColumnInfo(id='col_001', name='sales_amount', type='DECIMAL(10,2)', role='measure', examples=[199.99, 250.0], description='销售总金额', alias=['销售额', '营收'], table_id='tbl_order')

'''

2,2_test_column_info_mapper.py:

python 复制代码
# test_column_info_mapper.py
import unittest
from day05_Info_model.entities.column_info import ColumnInfo
from day05_Info_model.models.coloumn_info_mysql import ColumnInfoMySQL
from day05_Info_model.mappers.column_info_mapper import ColumnInfoMapper


class TestColumnInfoMapper(unittest.TestCase):

    def setUp(self):
        """测试前的准备工作:构造一个标准的业务实体"""
        self.entity = ColumnInfo(
            id="col_001",
            name="sales_amount",
            type="DECIMAL(10,2)",
            role="measure",
            examples=[199.99, 250.00],
            description="销售总金额",
            alias=["销售额", "营收"],
            table_id="tbl_order"
        )

    def test_to_model(self):
        """测试:业务实体 -> 数据库模型"""
        model = ColumnInfoMapper.to_model(self.entity)

        # 验证类型转换是否正确
        self.assertIsInstance(model, ColumnInfoMySQL)

        # 验证字段值是否完全一致
        self.assertEqual(model.id, "col_001")
        self.assertEqual(model.name, "sales_amount")
        self.assertEqual(model.type, "DECIMAL(10,2)")
        self.assertEqual(model.role, "measure")
        self.assertEqual(model.examples, [199.99, 250.00])
        self.assertEqual(model.description, "销售总金额")
        self.assertEqual(model.alias, ["销售额", "营收"])
        self.assertEqual(model.table_id, "tbl_order")

    def test_to_entity(self):
        """测试:数据库模型 -> 业务实体"""

        # 模拟从数据库查出来的对象(这里用字典模拟 SQLAlchemy 对象的行为)
        class MockDBModel:
            def __init__(self):
                self.id = "col_002"
                self.name = "user_name"
                self.type = "VARCHAR(64)"
                self.role = "dimension"
                self.examples = ["Alice", "Bob"]
                self.description = "用户姓名"
                self.alias = ["姓名"]
                self.table_id = "tbl_user"

        mock_model = MockDBModel()
        entity = ColumnInfoMapper.to_entity(mock_model)

        # 验证类型转换是否正确
        self.assertIsInstance(entity, ColumnInfo)

        # 验证字段值
        self.assertEqual(entity.id, "col_002")
        self.assertEqual(entity.role, "dimension")
        self.assertEqual(entity.alias, ["姓名"])

    def test_round_trip_conversion(self):
        """测试:双向转换(Entity -> Model -> Entity),确保数据不丢失"""
        model = ColumnInfoMapper.to_model(self.entity)
        converted_entity = ColumnInfoMapper.to_entity(model)

        # 验证经过一轮转换后,数据是否和原始实体一致
        self.assertEqual(self.entity.id, converted_entity.id)
        self.assertEqual(self.entity.name, converted_entity.name)
        self.assertEqual(self.entity.examples, converted_entity.examples)
        self.assertEqual(self.entity.alias, converted_entity.alias)


if __name__ == '__main__':
    unittest.main()
    
'''
点击pycharm运行弹出这样的窗口
这个错误 Error: Provide a qualified name of a function, class or module 是因为 PyCharm 的运行配置中,模块路径(Module Name)填写的格式不对。
PyCharm 试图把你输入的长字符串当作一个"模块"去加载,但它包含了类名和方法名,导致 Python 找不到这个模块。

✅ 解决方法(二选一)
方法一:修改当前配置(推荐)
请按照以下步骤调整你截图中的设置:
切换模式:将下拉菜单从 module 改为 Script path(脚本路径)。
选择文件:点击右侧的文件夹图标 📂,选择你的测试文件 2_test_column_info_mapper.py。
指定特定测试(可选):如果你想只运行这一个特定的测试方法,在下方的 Additional arguments(附加参数)框中输入:
text

编辑



TestColumnInfoMapper.test_to_entity
点击 Run。
方法二:最简单的"懒人"办法
不需要打开这个复杂的配置窗口:
关闭当前的 "Edit Configuration" 窗口。
回到代码编辑界面。
在代码左侧的行号栏旁边,找到 class TestColumnInfoMapper 或者 def test_to_entity 左边的 绿色三角形图标 ▶️。
直接点击那个绿色三角形,选择 "Run 'Unittests in ...'"。
PyCharm 会自动为你生成正确的配置并运行。
💡 为什么会报错?(原理解析)
在你截图的配置中:
Type: 选择了 module(这意味着后面只能填类似 unittest 或 my_package.my_module 这种纯包路径)。
Name: 你填了 ...TestColumnInfoMapper.test_to_entity。
Python 会尝试执行 import ...TestColumnInfoMapper.test_to_entity,这显然是不合法的导入语法,所以它提示你需要提供一个合法的"函数、类或模块名称"。
如果改完还报错,可以把新的报错截图发给我看看。
'''
相关推荐
煎饼学大模型1 小时前
Agent 的“大脑-手“解耦架构:当推理层和工具执行层各自独立演进
数据库·人工智能·oracle·架构·agent
muddjsv5 小时前
SQLite数据库文件机制详解:journal、wal、shm文件作用与备份避坑
数据库·sqlite
吴声子夜歌6 小时前
MongoDB 4.2——应用程序设计
数据库·mongodb
数字新视界6 小时前
动环监控可视化在数据中心管理中的应用与实践解析
数据库·物联网·解决方案·动环监控·大榕树
极地野狼 音乐哔哔6 小时前
数据库备份恢复的原理实践
数据库
互联网中的一颗神经元6 小时前
小白python入门 - 25. SQL 与表设计入门
数据库·python·sql
ttwuai6 小时前
AI 生成后台改数据后,操作日志别只记按钮:Go + MySQL 怎么验
数据库·人工智能·mysql·golang
KaMeidebaby7 小时前
卡梅德生物技术快报 | 核酸适配体文库测序:核酸适配体文库测序的技术原理、实验流程与数据解析
前端·网络·数据库·人工智能·算法
ClouGence7 小时前
除了 DBeaver,2026 年还有哪些好用的开源数据库管理工具?
数据库·开源