SQLAlchemy对象转字典¶
背景¶
已知一个模型对象,需要将其转成字典格式。
模型示例:
class User(Base):
"""用户模型Model"""
# 定义表名称
__tablename__ = 'user'
# 定义表字段
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String(30))
age: Mapped[int] = mapped_column(Integer)
执行查询:
user = db_session.query(User).first()
print(user)
上述打印输出对是对象信息,例如<__main__.User object at 0x104d9e140>
,展示结果不够直观,希望能将模型对象转成字典格式:把属性名作为字典键,把属性值作为字典值。
预期输出结果:
{'id': 1, 'name': '百里', 'age': 18}
实现¶
通过 __dict__
¶
- 思路
在 Python 中,可以通过Python内置函数dir()
来查看一个对象的所有属性和方法名列表,包括所有的魔法属性和普通成员属性。
obj = get_user()
print(dir(obj))
['__abstract__', '__annotations__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__mapper__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__table__', '__tablename__', '__weakref__', '_sa_class_manager', '_sa_instance_state', '_sa_registry', 'age', 'as_dict', 'id', 'metadata', 'name', 'registry']
其中,每个对象都有一个 __dict__
属性,它是一个字典,用于存储对象的所有属性和对应的值。
当我们访问一个对象的属性时,Python 解释器会先查找对象的 __dict__
属性,然后在 __dict__
中查找属性名对应的值。
执行代码:
obj = get_user()
print(obj.__dict__)
输出结果:
{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x104db0d60>, 'id': 1, 'name': '百里', 'age': 18}
可以看到,已经非常接近我们期望的字典。由于user
是SQLAlchemy
的对象,所以其中会有一些其他的属性比如_sa_instance
这种,过滤即可。
- 实现
首先,在User类中添加as_dict()
方法,返回对象的字典表示形式:
def as_dict(self):
return {k: v for k, v in self.__dict__.items() if not k.starts_with('_')}
然后执行:
obj = get_user()
print(obj.as_dict())
{'id': 1, 'name': '百里', 'age': 18}
可以看到已经获得了对象的字典表示形式。
完整代码:
from sqlalchemy import create_engine, String, Integer
from sqlalchemy.orm import declarative_base, mapped_column, Mapped, Session
# 基类
Base = declarative_base()
# 引擎
engine = create_engine('sqlite:///test.db', echo=True)
class User(Base):
"""用户模型Model"""
# 定义表名称
__tablename__ = 'user'
# 定义表字段
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String(30))
age: Mapped[int] = mapped_column(Integer)
def as_dict(self):
return {k: v for k, v in self.__dict__.items() if not k.startswith('_')}
def __init_db():
"""初始化数据库"""
# 建表
Base.metadata.create_all(engine)
# 实例化模型对象
user = User(
name='百里',
age=18
)
# 插入数据库
with Session(engine) as db_session:
db_session.add(user)
db_session.commit()
def get_user():
# 查询用户表记录
with Session(engine) as db_session:
user = db_session.query(User).first()
return user
if __name__ == '__main__':
obj = get_user()
# print(obj)
print(obj.as_dict())
通过 __table__
¶
__table__
是 SQLAlchemy 中一个重要的属性,用于将模型对象与数据库表相对应。通过指定__table__
属性,可以在模型对象中描述表的各个方面,例如表名、列名、主键等。__table__
属性通常与 SQLAlchemy 的 Table 对象一起使用,以提供对表的更多控制。
- 思路
在 SQLAlchemy 中,每个 ORM 模型类都对应一个数据库表。__table__
属性是 ORM 模型类中的一个特殊属性,用于指定模型类对应的数据库表的元信息,例如表名、列名、数据类型等。
因为表的字段名跟模型类对象的的属性名是相同的,因此可以考虑通过模型对应表的列信息,来间接组装成对象的属性和属性值字典。
obj = get_user()
print(obj.__table__.columns)
输出结果:
ReadOnlyColumnCollection(user.id, user.name, user.age)
- 实现
首先,在User
类中添加as_dict()
方法,返回对象的字典表示形式:
def as_dict(self):
return {c.name: getattr(self, c.name, None) for c in self.__table__.columns}
然后执行:
obj = get_user()
print(obj.as_dict())
{'id': 1, 'name': '百里', 'age': 18}
完整代码:
from sqlalchemy import create_engine, String, Integer
from sqlalchemy.orm import declarative_base, mapped_column, Mapped, Session
# 基类
Base = declarative_base()
# 引擎
engine = create_engine('sqlite:///test.db', echo=True)
class User(Base):
"""用户模型Model"""
# 定义表名称
__tablename__ = 'user'
# 定义表字段
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String(30))
age: Mapped[int] = mapped_column(Integer)
def as_dict(self):
return {c.name: getattr(self, c.name, None) for c in self.__table__.columns}
def __init_db():
"""初始化数据库"""
# 建表
Base.metadata.create_all(engine)
# 实例化模型对象
user = User(
name='百里',
age=18
)
# 插入数据库
with Session(engine) as db_session:
db_session.add(user)
db_session.commit()
def get_user():
# 查询用户表记录
with Session(engine) as db_session:
user = db_session.query(User).first()
return user
if __name__ == '__main__':
obj = get_user()
print(obj.as_dict())
通过 dataclass
¶
- 思路
dataclass
数据类是 Python 3.7 中新增的一个装饰器,用于简化创建和操作纯数据对象。通过使用 dataclass
装饰器,我们可以在不需要定义繁琐的 __init__
、__repr__
、__eq__
等方法的情况下,快速创建一个数据类。
使用 dataclass 装饰器定义的类可以通过调用dataclasses
本身提供的 asdict()
函数将实例转换成字典。这个函数将返回一个字典,其中包含了实例中的所有属性及其对应的值。
数据类示例:
from dataclasses import dataclass, asdict
@dataclass
class User:
id: int
name: str
age: int
user = User(1, '百里', 18)
print(asdict(user))
输出结果:
{'id': 1, 'name': '百里', 'age': 18}
- 实现
从dataclasses
导入dataclass
和asdict
方法,然后在User
模型类上使用@dataclass
进行装饰。
from dataclasses import dataclass, asdict
@dataclass
class User(Base):
"""用户模型Model"""
# 定义表名称
__tablename__ = 'user'
# 定义表字段
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String(30))
age: Mapped[int] = mapped_column(Integer)
这样一来,就可以直接调用数据类提供对as_dict(obj)
方法,便捷地将模型类的对象转换成字典形式。
执行代码
obj = get_user()
print(asdict(obj))
输出结果:
{'id': 1, 'name': '百里', 'age': 18}
完整代码:
from sqlalchemy import create_engine, String, Integer
from sqlalchemy.orm import declarative_base, mapped_column, Mapped, Session
from dataclasses import dataclass, asdict
# 基类
Base = declarative_base()
# 引擎
engine = create_engine('sqlite:///test.db', echo=True)
@dataclass
class User(Base):
"""用户模型Model"""
# 定义表名称
__tablename__ = 'user'
# 定义表字段
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String(30))
age: Mapped[int] = mapped_column(Integer)
def __init_db():
"""初始化数据库"""
# 建表
Base.metadata.create_all(engine)
# 实例化模型对象
user = User(
name='百里',
age=18
)
# 插入数据库
with Session(engine) as db_session:
db_session.add(user)
db_session.commit()
def get_user():
# 查询用户表记录
with Session(engine) as db_session:
user = db_session.query(User).first()
return user
if __name__ == '__main__':
obj = get_user()
print(asdict(obj))