一、Pony ORM 核心概述
Pony ORM 是一款面向 Python 开发者的对象关系映射工具,它能将数据库表结构映射为 Python 类,让开发者以操作对象的方式替代原生 SQL 语句完成数据库的增删改查。其工作原理是将 Python 代码转换为对应的 SQL 语句,再与数据库交互并返回结果。该库支持 SQLite、MySQL、PostgreSQL 等主流数据库,License 为 Apache License 2.0。

Pony ORM 的优点十分突出:语法简洁直观,贴近 Python 原生风格;支持自动生成数据库表结构;内置数据验证机制;查询性能优秀且支持懒加载。缺点则是对复杂 SQL 语句的支持灵活性稍弱,在超大规模分布式数据库场景下的适配性不如专业级 ORM 框架。
二、Pony ORM 安装步骤
对于技术小白来说,Pony ORM 的安装流程非常简单,只需要使用 Python 自带的包管理工具 pip 即可完成。无论是 Windows、Mac 还是 Linux 系统,操作命令完全一致。
2.1 基础安装命令
打开系统的命令行终端(Windows 是 CMD 或 PowerShell,Mac 和 Linux 是 Terminal),输入以下命令:
pip install pony
这条命令会自动从 PyPI 下载并安装最新版本的 Pony ORM 库。
2.2 验证安装是否成功
安装完成后,我们可以在 Python 交互式环境中验证是否安装成功。在终端输入 python 进入交互模式,然后执行以下代码:
import pony
print(pony.__version__)
如果终端能够输出 Pony ORM 的版本号(例如 0.7.16),则说明安装成功。如果出现 ModuleNotFoundError 错误,则需要检查 pip 命令是否执行正确,或者 Python 环境是否存在冲突。
三、Pony ORM 核心使用方法
Pony ORM 的核心使用流程分为三步:定义数据库映射类、连接数据库、执行数据库操作。下面我们以最常用的 SQLite 数据库为例(无需额外配置服务,文件型数据库更适合新手),详细讲解每一步的操作方法并搭配实例代码。
3.1 定义数据库映射类与连接数据库
首先,我们需要创建一个 Database 对象来连接数据库,然后通过继承 db.Entity 来定义数据表对应的 Python 类,类中的属性对应数据表的字段。
实例代码:定义学生信息表
from pony.orm import *
# 1. 创建Database对象,连接SQLite数据库
# 参数说明:provider指定数据库类型,filename指定数据库文件路径,create_db=True表示如果文件不存在则自动创建
db = Database()
# 2. 定义映射类,对应数据库中的"Student"表
class Student(db.Entity):
# 定义字段,primary_key=True表示该字段为主键
id = PrimaryKey(int, auto=True) # 自增整数主键
name = Required(str, max_len=50) # 必填字符串字段,最大长度50
age = Required(int) # 必填整数字段
gender = Optional(str, max_len=10, default="未知") # 可选字符串字段,默认值为"未知"
score = Optional(float) # 可选浮点数字段,存储学生成绩
# 3. 绑定数据库并生成数据表
# 参数说明:db_session=True表示自动管理数据库会话
db.bind(provider='sqlite', filename='school.db', create_db=True)
# 创建所有定义的实体对应的表,如果表已存在则不会重复创建
db.generate_mapping(create_tables=True)
代码说明
Database()是 Pony ORM 的核心对象,负责管理数据库连接和实体映射关系。- 继承
db.Entity的类会被 Pony ORM 识别为数据表映射类,类名默认对应数据库中的表名(也可以通过_table_属性自定义表名)。 - 字段类型说明:
PrimaryKey:定义主键字段,auto=True表示自动递增。Required:定义必填字段,必须传入值才能创建记录。Optional:定义可选字段,可以不传入值,支持设置默认值。
db.bind()方法用于绑定具体的数据库,这里选择 SQLite 数据库,school.db是数据库文件的名称,会保存在当前代码运行的目录下。db.generate_mapping()方法用于根据定义的实体类生成数据库表结构,create_tables=True表示如果表不存在则自动创建。
3.2 数据库会话管理
Pony ORM 要求所有数据库操作都必须在 数据库会话 中执行,常用的会话管理方式有两种:装饰器方式和上下文管理器方式,两种方式都能自动完成会话的开启、提交和关闭操作,非常适合新手使用。
方式1:使用 @db_session 装饰器
这是最常用的方式,将装饰器添加在执行数据库操作的函数上即可。
@db_session
def add_student():
# 在会话中执行数据库操作
s1 = Student(name="张三", age=18, gender="男", score=92.5)
s2 = Student(name="李四", age=19, gender="女", score=88.0)
s3 = Student(name="王五", age=17, score=95.0) # gender使用默认值"未知"
# 无需手动提交,装饰器会自动提交事务
# 调用函数执行数据插入
add_student()
方式2:使用 db_session 上下文管理器
适合在代码块中执行数据库操作,使用 with 语句包裹操作代码。
with db_session:
s4 = Student(name="赵六", age=20, gender="男", score=85.5)
# 代码块结束后自动提交事务
代码说明
- 无论是装饰器还是上下文管理器,都不需要手动调用
commit()提交事务,也不需要手动关闭会话,Pony ORM 会自动处理。 - 如果在操作过程中出现异常,会话会自动回滚,保证数据一致性。
3.3 数据查询操作
查询是数据库操作中最常用的功能,Pony ORM 提供了简洁的查询语法,支持条件查询、排序、分页、聚合等多种操作,完全可以替代原生 SQL 语句。
3.3.1 基础查询:获取所有记录
使用 select() 函数可以查询数据表中的记录,返回的是一个可迭代的 Query 对象,可以直接通过 for 循环遍历。
@db_session
def query_all_students():
# 查询所有学生记录,select(s for s in Student) 等价于 SQL: SELECT * FROM Student
students = select(s for s in Student)
# 遍历查询结果
for s in students:
print(f"ID: {s.id}, 姓名: {s.name}, 年龄: {s.age}, 性别: {s.gender}, 成绩: {s.score}")
# 调用函数查询数据
query_all_students()
3.3.2 条件查询:筛选指定记录
在 select() 函数中添加条件表达式,可以筛选出符合要求的记录,条件表达式的写法和 Python 原生语法一致,非常容易理解。
@db_session
def query_student_by_condition():
# 条件1:查询年龄大于18岁的学生
students1 = select(s for s in Student if s.age > 18)
print("年龄大于18岁的学生:")
for s in students1:
print(f"{s.name} - 年龄: {s.age}")
# 条件2:查询性别为男且成绩大于90分的学生
students2 = select(s for s in Student if s.gender == "男" and s.score > 90)
print("\n性别为男且成绩大于90分的学生:")
for s in students2:
print(f"{s.name} - 成绩: {s.score}")
# 条件3:查询姓名包含"张"字的学生(模糊查询)
students3 = select(s for s in Student if "张" in s.name)
print("\n姓名包含张字的学生:")
for s in students3:
print(f"{s.name} - ID: {s.id}")
# 调用函数执行条件查询
query_student_by_condition()
代码说明
- Pony ORM 会自动将条件表达式转换为对应的 SQL WHERE 子句,例如
s.age > 18对应WHERE age > 18。 - 模糊查询可以直接使用 Python 的
in关键字,等价于 SQL 中的LIKE语句。
3.3.3 排序与分页查询
在实际开发中,查询结果往往需要排序和分页,Pony ORM 提供了 order_by() 和 limit()、offset() 方法来实现这两个功能。
@db_session
def query_student_with_sort_and_page():
# 1. 按成绩降序排序(从高到低)
sorted_students = select(s for s in Student).order_by(desc(s.score))
print("按成绩降序排序的学生:")
for s in sorted_students:
print(f"{s.name} - 成绩: {s.score}")
# 2. 分页查询:获取第2页的数据,每页2条记录
page_size = 2
page_num = 2
# offset表示跳过的记录数,计算方式:(页码-1)*每页条数
paged_students = select(s for s in Student).order_by(s.id).limit(page_size).offset((page_num-1)*page_size)
print(f"\n第{page_num}页数据(每页{page_size}条):")
for s in paged_students:
print(f"{s.id} - {s.name}")
# 调用函数执行排序和分页查询
query_student_with_sort_and_page()
代码说明
order_by()方法用于排序,传入desc(字段)表示降序,直接传入字段名表示升序。limit()方法用于限制查询结果的数量,offset()方法用于跳过指定数量的记录,两者结合实现分页功能。
3.3.4 聚合查询:统计数据
Pony ORM 支持常用的聚合函数,例如 count()、sum()、avg() 等,可以方便地实现数据统计功能。
@db_session
def aggregate_student_data():
# 1. 统计学生总数
total = count(s for s in Student)
print(f"学生总数:{total}")
# 2. 统计所有学生的平均成绩
avg_score = avg(s.score for s in Student if s.score is not None)
print(f"学生平均成绩:{avg_score:.2f}")
# 3. 统计男生的最高成绩
max_score_male = max(s.score for s in Student if s.gender == "男")
print(f"男生最高成绩:{max_score_male}")
# 4. 统计女生的总成绩
sum_score_female = sum(s.score for s in Student if s.gender == "女")
print(f"女生总成绩:{sum_score_female}")
# 调用函数执行聚合查询
aggregate_student_data()
代码说明
聚合函数可以直接作用于 select() 生成的 Query 对象,也可以直接通过函数调用的方式使用,语法简洁易懂,不需要编写复杂的 SQL 聚合语句。
3.4 数据更新与删除操作
除了查询,Pony ORM 也支持便捷的数据更新和删除操作,操作方式同样是通过操作 Python 对象来实现。
3.4.1 数据更新
更新数据只需要在会话中获取对应的对象,修改其属性值,会话结束时会自动提交更新。
@db_session
def update_student():
# 1. 根据ID获取学生对象
student = Student.get(id=1) # get()方法用于根据主键获取单个对象
if student:
# 2. 修改对象属性
student.age = 19
student.score = 96.0
print(f"更新后的数据:{student.name} - 年龄: {student.age}, 成绩: {student.score}")
else:
print("未找到ID为1的学生")
# 调用函数更新数据
update_student()
代码说明
Student.get(id=1) 等价于 SQL 语句 SELECT * FROM Student WHERE id=1 LIMIT 1,返回的是单个对象而不是 Query 对象,适合根据主键查询单条记录。
3.4.2 数据删除
删除数据的方式有两种:一是获取对象后调用 delete() 方法,二是直接通过 Query 对象调用 delete() 方法批量删除。
@db_session
def delete_student():
# 方式1:删除单个对象
student = Student.get(id=4)
if student:
student.delete()
print(f"已删除学生:{student.name}")
# 方式2:批量删除符合条件的对象
# 删除年龄小于18岁的学生
delete_count = delete(s for s in Student if s.age < 18)
print(f"批量删除了{delete_count}条记录")
# 调用函数删除数据
delete_student()
代码说明
- 单个对象删除:调用对象的
delete()方法即可。 - 批量删除:使用
delete()函数配合条件表达式,返回值是删除的记录条数。
3.5 一对多关系映射
在实际的数据库设计中,表与表之间往往存在关联关系,例如“班级”和“学生”的一对多关系(一个班级有多个学生,一个学生属于一个班级)。Pony ORM 可以轻松实现这种关联关系的映射。
实例代码:定义班级与学生的一对多关系
from pony.orm import *
db = Database()
# 定义班级类(一的一方)
class Class(db.Entity):
id = PrimaryKey(int, auto=True)
name = Required(str, max_len=30) # 班级名称
# 定义一对多关系,Set表示多个学生对象,reverse表示反向引用(学生对象可以通过class属性访问班级)
students = Set("Student", reverse="class_")
# 定义学生类(多的一方)
class Student(db.Entity):
id = PrimaryKey(int, auto=True)
name = Required(str, max_len=50)
age = Required(int)
# 定义外键关联,Required表示每个学生必须属于一个班级
class_ = Required(Class, reverse="students")
# 绑定数据库并生成表
db.bind(provider='sqlite', filename='school_relation.db', create_db=True)
db.generate_mapping(create_tables=True)
# 插入测试数据
@db_session
def add_relation_data():
# 创建两个班级
c1 = Class(name="高一(1)班")
c2 = Class(name="高一(2)班")
# 创建学生并关联到班级
s1 = Student(name="张三", age=16, class_=c1)
s2 = Student(name="李四", age=16, class_=c1)
s3 = Student(name="王五", age=17, class_=c2)
# 查询关联数据
@db_session
def query_relation_data():
# 1. 查询班级对应的所有学生
class1 = Class.get(name="高一(1)班")
print(f"班级:{class1.name} 的学生列表:")
for s in class1.students:
print(f"- {s.name}")
# 2. 查询学生所属的班级
student = Student.get(name="王五")
print(f"\n{student.name} 所属班级:{student.class_.name}")
# 调用函数执行操作
add_relation_data()
query_relation_data()
代码说明
- 一对多关系的核心是
Set和Required的配合使用:- 一的一方(Class)使用
Set("Student")表示该班级拥有多个学生。 - 多的一方(Student)使用
Required(Class)表示每个学生必须属于一个班级。
- 一的一方(Class)使用
reverse参数用于设置反向引用的属性名,方便通过学生对象访问班级信息。
四、Pony ORM 实际应用案例:学生成绩管理系统
为了帮助大家更好地理解 Pony ORM 在实际项目中的应用,我们来开发一个简单的学生成绩管理系统,该系统实现以下功能:
- 添加学生信息和成绩
- 查询指定学生的成绩
- 更新学生成绩
- 统计班级的平均成绩
- 删除不及格的学生记录
4.1 完整案例代码
from pony.orm import *
# 1. 初始化数据库连接
db = Database()
# 2. 定义实体类
class Class(db.Entity):
id = PrimaryKey(int, auto=True)
name = Required(str, max_len=30, unique=True) # 班级名称唯一
students = Set("Student", reverse="class_")
class Student(db.Entity):
id = PrimaryKey(int, auto=True)
name = Required(str, max_len=50)
age = Required(int)
score = Required(float) # 成绩字段
class_ = Required(Class, reverse="students")
# 3. 绑定数据库并生成表
db.bind(provider='sqlite', filename='student_score_manage.db', create_db=True)
db.generate_mapping(create_tables=True)
# 4. 系统功能函数
@db_session
def add_class(class_name):
"""添加班级"""
try:
Class(name=class_name)
print(f"班级 {class_name} 添加成功!")
except UniqueError:
print(f"班级 {class_name} 已存在!")
@db_session
def add_student(name, age, score, class_name):
"""添加学生信息"""
class_ = Class.get(name=class_name)
if not class_:
print(f"班级 {class_name} 不存在,请先添加班级!")
return
Student(name=name, age=age, score=score, class_=class_)
print(f"学生 {name} 添加成功!")
@db_session
def query_student_score(name):
"""查询指定学生的成绩"""
student = Student.get(name=name)
if student:
print(f"学生:{student.name}")
print(f"班级:{student.class_.name}")
print(f"成绩:{student.score}")
else:
print(f"未找到学生 {name} 的信息!")
@db_session
def update_student_score(name, new_score):
"""更新学生成绩"""
student = Student.get(name=name)
if student:
student.score = new_score
print(f"学生 {name} 的成绩已更新为 {new_score}")
else:
print(f"未找到学生 {name} 的信息!")
@db_session
def calculate_class_avg_score(class_name):
"""统计班级平均成绩"""
class_ = Class.get(name=class_name)
if not class_:
print(f"班级 {class_name} 不存在!")
return
avg_score = avg(s.score for s in class_.students)
print(f"班级 {class_name} 的平均成绩为:{avg_score:.2f}")
@db_session
def delete_failed_students():
"""删除成绩低于60分的学生"""
delete_count = delete(s for s in Student if s.score < 60)
print(f"共删除了 {delete_count} 名不及格的学生记录")
# 5. 测试系统功能
if __name__ == "__main__":
# 添加班级
add_class("高一(1)班")
add_class("高一(2)班")
# 添加学生
add_student("张三", 16, 92.5, "高一(1)班")
add_student("李四", 16, 85.0, "高一(1)班")
add_student("王五", 17, 58.0, "高一(2)班")
add_student("赵六", 17, 76.5, "高一(2)班")
# 查询学生成绩
print("\n===== 查询学生成绩 =====")
query_student_score("张三")
# 更新学生成绩
print("\n===== 更新学生成绩 =====")
update_student_score("李四", 88.5)
query_student_score("李四")
# 统计班级平均成绩
print("\n===== 统计班级平均成绩 =====")
calculate_class_avg_score("高一(1)班")
# 删除不及格学生
print("\n===== 删除不及格学生 =====")
delete_failed_students()
# 查看删除后的班级平均成绩
print("\n===== 删除后班级平均成绩 =====")
calculate_class_avg_score("高一(2)班")
4.2 代码运行结果
班级 高一(1)班 添加成功!
班级 高一(2)班 添加成功!
学生 张三 添加成功!
学生 李四 添加成功!
学生 王五 添加成功!
学生 赵六 添加成功!
===== 查询学生成绩 =====
学生:张三
班级:高一(1)班
成绩:92.5
===== 更新学生成绩 =====
学生 李四 的成绩已更新为 88.5
学生:李四
班级:高一(1)班
成绩:88.5
===== 统计班级平均成绩 =====
班级 高一(1)班 的平均成绩为:90.50
===== 删除不及格学生 =====
共删除了 1 名不及格的学生记录
===== 删除后班级平均成绩 =====
班级 高一(2)班 的平均成绩为:76.50
4.3 案例说明
这个学生成绩管理系统虽然简单,但涵盖了 Pony ORM 的核心操作,包括实体定义、关联关系、增删改查和聚合统计。在实际开发中,我们可以基于这个框架扩展更多功能,例如添加课程表、教师表,实现更复杂的多表关联查询。
五、Pony ORM 相关资源
- PyPI 地址:https://pypi.org/project/Pony
- Github 地址:https://github.com/ponyorm/pony
- 官方文档地址:https://docs.ponyorm.org/
关注我,每天分享一个实用的Python自动化工具。

