SQLAlchemy中模型属性与数据库列名的映射及键值对生成问题
看起来你遇到的核心问题是如何自动把SQLAlchemy模型实例的属性值对应到数据库列名上,生成替换模板所需的字典——完全理解不想手动写映射的心情,手动维护太容易出错了!
先帮你理清之前的问题出在哪:你用retrieved_table_entity.__table__.columns遍历的是数据库表的列定义,这里的column.key其实是数据库列名(比如Id),而不是你模型里的蛇形属性名(比如id),所以才会得到{'Id': 'Id'}这种没用的映射。
正确的思路是通过SQLAlchemy的映射器(mapper)来关联模型属性和数据库列名,下面给你两种靠谱的实现方式:
方法一:用SQLAlchemy的inspect工具(推荐)
inspect是SQLAlchemy专门用来获取模型/实例元数据的工具,用它可以轻松拿到模型属性和对应列名的关联关系:
from sqlalchemy import inspect # 先获取你的模型实例 retrieved_table_entity = self.repository.get_entry(order_id, something) # 获取模型的映射器元数据 model_mapper = inspect(retrieved_table_entity.__class__) # 生成列名到值的字典 column_values = {} for attr in model_mapper.attrs: # 获取数据库列名 column_name = attr.columns[0].name # 获取模型实例对应的属性值 value = getattr(retrieved_table_entity, attr.key) # 处理空值,转成字符串 column_values[column_name] = '' if value is None else str(value)
如果喜欢更简洁的推导式写法:
from sqlalchemy import inspect retrieved_table_entity = self.repository.get_entry(order_id, something) model_mapper = inspect(retrieved_table_entity.__class__) column_values = { attr.columns[0].name: '' if getattr(retrieved_table_entity, attr.key) is None else str(getattr(retrieved_table_entity, attr.key)) for attr in model_mapper.attrs }
方法二:利用模型的__dict__(轻量方式)
如果你不想引入inspect,也可以直接从模型实例的__dict__里取属性值,但要注意过滤掉SQLAlchemy的内部属性(比如_sa_instance_state),再结合提前生成的属性-列名映射:
# 提前生成模型属性到列名的映射(可以放在模型类里或者全局) from sqlalchemy import inspect attr_to_column = { attr.key: attr.columns[0].name for attr in inspect(TableName).attrs } # 反转成列名到属性名的映射 column_to_attr = {v: k for k, v in attr_to_column.items()} # 生成最终字典 retrieved_table_entity = self.repository.get_entry(order_id, something) column_values = { col_name: '' if getattr(retrieved_table_entity, attr_name) is None else str(getattr(retrieved_table_entity, attr_name)) for col_name, attr_name in column_to_attr.items() }
这样生成的column_values字典就是你想要的格式,直接用self.template.format(**column_values)就能替换模板里的占位符了。
最后测试一下:比如你的模型实例retrieved_table_entity.id是1,retrieved_table_entity.order_id是123,那生成的字典就会是{'Id': '1', 'OrderId': '123', 'ColumnName1': 'SomeValue', ...},完美匹配你的模板需求。
备注:内容来源于stack exchange,提问作者Carmageddon

