Flask中ORM的使用

Flask中ORM的使用

本文介绍Flask中ORM框架flask_sqlalchemy的基本使用,包含模型定义(简单模型,一对一,一对多,多对多等),由于实际开发中很少使用物理外键,所有本文所有模型都不使用物理外键,而关联关系db.relationship是应用层面的,故仍然使用。以下模型以学生、课程、班级为例,这三者基本涵盖了所有的模型对应关系。

模型定义(不使用物理外键)

1. 基础模型

from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.ext.associationproxy import association_proxydb = SQLAlchemy()class Student(db.Model):"""学生模型"""__tablename__ = 'students'id = db.Column(db.Integer, primary_key=True)name = db.Column(db.String(80), nullable=False)class_id = db.Column(db.Integer)  # 不使用物理外键# 关系属性class_rel = db.relationship('Class',primaryjoin='Class.id == Student.class_id',viewonly=True,back_populates='students_rel')# 关联代理class_name = association_proxy('class_rel', 'name')def __repr__(self):return f'<Student {self.name}>'class Class(db.Model):"""班级模型"""__tablename__ = 'classes'id = db.Column(db.Integer, primary_key=True)name = db.Column(db.String(50), unique=True, nullable=False)# 关系属性students_rel = db.relationship('Student',primaryjoin='Class.id == Student.class_id',viewonly=True,back_populates='class_rel')# 关联代理students = association_proxy('students_rel', 'self')student_names = association_proxy('students_rel', 'name')def __repr__(self):return f'<Class {self.name}>'class Course(db.Model):"""课程模型"""__tablename__ = 'courses'id = db.Column(db.Integer, primary_key=True)name = db.Column(db.String(100), unique=True, nullable=False)credit = db.Column(db.Integer, default=1)def __repr__(self):return f'<Course {self.name}>'

2. 多对多关联模型

# 学生-课程关联表(不使用物理外键)
class StudentCourse(db.Model):"""学生-课程关联模型"""__tablename__ = 'student_courses'id = db.Column(db.Integer, primary_key=True)student_id = db.Column(db.Integer, nullable=False)course_id = db.Column(db.Integer, nullable=False)score = db.Column(db.Float)  # 成绩# 学生关系student_rel = db.relationship('Student',primaryjoin='Student.id == StudentCourse.student_id',viewonly=True)# 课程关系course_rel = db.relationship('Course',primaryjoin='Course.id == StudentCourse.course_id',viewonly=True)# 关联代理student_name = association_proxy('student_rel', 'name')course_name = association_proxy('course_rel', 'name')def __repr__(self):return f'<StudentCourse student:{self.student_id} course:{self.course_id}>'# 为Student添加课程关系
Student.courses = db.relationship('StudentCourse',primaryjoin='Student.id == StudentCourse.student_id',backref='student',viewonly=True
)# 为Course添加学生关系
Course.students = db.relationship('StudentCourse',primaryjoin='Course.id == StudentCourse.course_id',backref='course',viewonly=True
)

查询操作

1. 简单查询(带过滤条件)

# 查询所有学生
all_students = Student.query.all()# 查询学分大于2的课程
high_credit_courses = Course.query.filter(Course.credit > 2).all()# 查询姓"张"的学生
zhang_students = Student.query.filter(Student.name.like('张%')).all()# 分页查询班级
page = Class.query.paginate(page=1, per_page=10, error_out=False)

2. 一对一关系查询(学生-班级)

使用 relationship 方式:

# 查询学生及其班级名称
student = Student.query.get(1)
print(f"学生: {student.name}, 班级: {student.class_name}")# 查询班级及其所有学生
class_obj = Class.query.get(101)
for student in class_obj.students:print(f"班级 {class_obj.name} 的学生: {student.name}")

使用 JOIN 方式:

# 查询学生及其班级信息
result = db.session.query(Student, Class.name)\.join(Class, Student.class_id == Class.id)\.filter(Student.id == 1)\.first()if result:student, class_name = resultprint(f"学生: {student.name}, 班级: {class_name}")# 查询班级及其学生数量
from sqlalchemy import funcclass_info = db.session.query(Class.name,func.count(Student.id).label('student_count')
).join(Student, Class.id == Student.class_id).group_by(Class.id).all()

3. 一对多关系查询(班级-学生)

使用 relationship 方式:

# 查询班级及其所有学生
class_obj = Class.query.get(101)
print(f"班级: {class_obj.name}")
for student in class_obj.students:print(f" - 学生: {student.name}")# 使用预加载优化
classes = Class.query.options(db.joinedload(Class.students_rel)).all()

使用 JOIN 方式:

# 查询班级及其学生
results = db.session.query(Class.name, Student.name)\.join(Student, Class.id == Student.class_id)\.filter(Class.id == 101)\.all()for class_name, student_name in results:print(f"班级: {class_name}, 学生: {student_name}")# 查询每个班级的学生数量
class_counts = db.session.query(Class.name,func.count(Student.id).label('count').outerjoin(Student, Class.id == Student.class_id).group_by(Class.id).order_by(db.desc('count')).all()

4. 多对多关系查询(学生-课程)

使用 relationship 方式:

# 查询学生的所有课程
student = Student.query.get(1)
for sc in student.courses:  # sc 是 StudentCourse 对象print(f"课程: {sc.course_name}, 成绩: {sc.score or '未录入'}")# 查询课程的所有学生
course = Course.query.get(201)
for sc in course.students:print(f"学生: {sc.student_name}, 成绩: {sc.score or '未录入'}")# 使用关联代理直接获取课程名称
student = Student.query.get(1)
course_names = [sc.course_name for sc in student.courses]
print(f"学生 {student.name} 的课程: {', '.join(course_names)}")

使用 JOIN 方式:

# 查询学生及其课程成绩
results = db.session.query(Student.name, Course.name, StudentCourse.score)\.join(StudentCourse, Student.id == StudentCourse.student_id)\.join(Course, Course.id == StudentCourse.course_id)\.filter(Student.id == 1)\.all()for student_name, course_name, score in results:print(f"学生: {student_name}, 课程: {course_name}, 成绩: {score}")# 查询每门课程的选修人数
course_stats = db.session.query(Course.name,func.count(StudentCourse.student_id).label('student_count'),func.avg(StudentCourse.score).label('avg_score'))\.join(StudentCourse, Course.id == StudentCourse.course_id)\.group_by(Course.id)\.order_by(db.desc('student_count'))\.all()

新增操作

1. 简单模型的单个新增

# 新增班级
new_class = Class(name="计算机科学2023级")
db.session.add(new_class)
db.session.commit()# 新增学生
new_student = Student(name="张三", class_id=new_class.id)
db.session.add(new_student)
db.session.commit()# 新增课程
new_course = Course(name="数据库原理", credit=3)
db.session.add(new_course)
db.session.commit()

2. 简单模型的批量新增

# 批量新增学生
students_data = [{"name": "李四", "class_id": new_class.id},{"name": "王五", "class_id": new_class.id},{"name": "赵六", "class_id": new_class.id}
]students = [Student(**data) for data in students_data]
db.session.add_all(students)
db.session.commit()# 批量新增课程
courses_data = [{"name": "数据结构", "credit": 4},{"name": "算法设计", "credit": 3},{"name": "操作系统", "credit": 4}
]courses = [Course(**data) for data in courses_data]
db.session.add_all(courses)
db.session.commit()

3. 关联关系新增

# 学生选课(添加多对多关系)
# 获取学生和课程
student = Student.query.filter_by(name="张三").first()
course1 = Course.query.filter_by(name="数据库原理").first()
course2 = Course.query.filter_by(name="数据结构").first()# 添加选课记录
sc1 = StudentCourse(student_id=student.id, course_id=course1.id, score=92.5)
sc2 = StudentCourse(student_id=student.id, course_id=course2.id)db.session.add_all([sc1, sc2])
db.session.commit()

修改操作

1. 简单模型的修改

# 修改学生信息
student = Student.query.get(1)
if student:student.name = "张三丰"  # 修改姓名db.session.commit()# 修改课程学分
Course.query.filter_by(name="数据库原理").update({"credit": 4})
db.session.commit()

2. 条件修改

# 为所有2023级班级的学生增加学分(假设有class_year字段)
# 先找到2023级班级
class_2023 = Class.query.filter(Class.name.like("%2023级")).all()
class_ids = [c.id for c in class_2023]# 为这些班级的所有学生增加一门选修课
new_course = Course.query.filter_by(name="人工智能导论").first()if new_course:# 找出这些班级中还没选修该课程的学生students = Student.query.filter(Student.class_id.in_(class_ids),~Student.id.in_(db.session.query(StudentCourse.student_id).filter(StudentCourse.course_id == new_course.id))).all()# 为这些学生添加选课记录new_records = [StudentCourse(student_id=s.id, course_id=new_course.id)for s in students]db.session.add_all(new_records)db.session.commit()

删除操作

1. 简单模型的单个删除

# 删除一个学生
student = Student.query.get(1)
if student:# 先删除关联的选课记录StudentCourse.query.filter_by(student_id=student.id).delete()# 再删除学生db.session.delete(student)db.session.commit()# 删除一门课程
course = Course.query.get(201)
if course:# 先删除关联的选课记录StudentCourse.query.filter_by(course_id=course.id).delete()# 再删除课程db.session.delete(course)db.session.commit()

2. 简单模型的批量删除

# 删除所有没有选修任何课程的学生
# 先找出没有选课的学生
no_course_students = Student.query.filter(~Student.id.in_(db.session.query(StudentCourse.student_id))
).all()# 批量删除
for student in no_course_students:db.session.delete(student)db.session.commit()# 删除空班级
empty_classes = db.session.query(Class)\.outerjoin(Student, Class.id == Student.class_id)\.group_by(Class.id)\.having(func.count(Student.id) == 0)\.all()for class_obj in empty_classes:db.session.delete(class_obj)db.session.commit()

最佳实践总结

1. 模型设计建议

  • 明确关系类型:准确区分一对一、一对多、多对多关系
  • 使用关联代理:简化关联属性访问,如student.class_name
  • 添加索引:对经常查询的字段添加索引
  • 避免循环导入:将模型定义放在单独文件中

2. 查询优化技巧

  • N+1问题:始终使用joinedloadselectinload预加载关联数据
  • 按需加载:使用load_only限制返回字段
  • 批量操作:优先使用批量查询和操作,减少数据库交互次数
  • 分页处理:对大数据集使用分页查询

3. 事务管理

try:# 执行数据库操作db.session.commit()
except Exception as e:db.session.rollback()# 处理异常
finally:db.session.close()

4. 性能监控

# 启用SQL日志
app.config['SQLALCHEMY_ECHO'] = True# 使用EXPLAIN分析慢查询
slow_query = Student.query.filter(Student.name.like('张%'))
explain = db.session.execute(f"EXPLAIN ANALYZE {str(slow_query.statement)}")
for line in explain:print(line[0])

5. 安全注意事项

  • 参数化查询:始终使用ORM查询或参数化SQL,防止SQL注入
  • 数据验证:在提交前验证所有输入数据
  • 权限控制:实现细粒度的数据访问控制

通过本指南,您应该能够全面掌握在不使用物理外键的情况下,如何设计ORM模型并执行各种数据库操作。学生-班级-课程的示例覆盖了大多数实际开发场景,可作为您项目开发的参考模板。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/93158.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/93158.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FPGA即插即用Verilog驱动系列——高速12位ADC

实现功能&#xff1a;单通道ADC驱动&#xff0c;速率由驱动的时钟决定12位数据并行&#xff0c;可轻松修改为其他位宽&#xff0c;适应不同的ADC模块将ADC输入的unsigned数据转换为signed&#xff0c;便于后续FIR&#xff0c;MULTI操作匹配AXI4-STREAM协议&#xff0c;有tvalid…

DeepSeek 部署中的常见问题及解决方案:从环境配置到性能优化的全流程指南

一、引言随着大模型技术的发展&#xff0c;以 DeepSeek 为代表的开源中文大模型&#xff0c;逐渐成为企业与开发者探索私有化部署、垂直微调、模型服务化的重要选择。然而&#xff0c;模型部署的过程并非 “一键启动” 那么简单。从环境依赖、资源限制&#xff0c;到推理性能和…

【机器人-开发工具】ROS 2 (4)Jetson Nano 系统Ubuntu22.04安装ROS 2 Humble版本

文章目录1. 系统环境准备1.1. Jetpack简介1.2. 下载Jetpack安装系统2. 安装ROS2 Humble2.1. ROS2 简介2.2. ROS2 Humble对比Foxy版本2.3. 安装2.3.1. 更新系统2.3.2. 添加 ROS 2 GPG 密钥2.3.3. 添加 ROS 2 仓库源2.3.4. 更新软件包索引2.3.5. 安装 ROS 2 Humble 桌面版&#x…

2025年Java大厂面试场景题全解析:高频考点与实战攻略

一、2025年Java面试新趋势与技术栈变化2025年的Java技术生态呈现出明显的云原生与AI集成趋势&#xff0c;各大互联网公司在面试中更加注重候选人对新技术栈的掌握程度和实战应用能力。1.1 技术栈升级趋势分析根据最新统计数据&#xff0c;2025年Java面试的技术考察点分布如下&a…

TCP客户端Linux网络编程设计详解

一、TCP 客户端设计流程TCP客户端模式的程序设计流程主要分为&#xff1a;套接字初始化( socket()函数)&#xff0c;连接目标网络服务器 (connect()函数)&#xff0c;向服务器端写入数据&#xff08;write()函数&#xff09;1、socket() 函数#include <sys/types.h> …

webpack》》

Webpark 介绍 官网 Webpack的功能 在现代前端开发中,我们会使用模块化、Sass、TypeScript、图片、字体等资源。但浏览器并不天然支持这些格式,因此我们需要工具将它们打包、转换成浏览器能识别的文件格式。Webpack 就是这样一个强大的前端构建工具。 Webpack 是一个现代 J…

软件测评中HTTP 安全头的配置与测试规范

服务器若缺乏必要的安全头配置&#xff0c;其安全防护能力将大幅降低。X-Content-Type-Options 作为基础安全头&#xff0c;需设置 nosniff 参数&#xff0c;以阻止浏览器对 MIME 类型进行自主猜测&#xff0c;避免 text/css 等资源被误当作脚本执行&#xff0c;从源头切断此类…

5G专网项目外场常见业务测试指南(六)-PingInfoView

5G项目必然涉及到终端用户的使用&#xff0c;终端使用情况测试最常用的手段就是长时间7*24小时长ping&#xff0c;对于一个有着几百用户的5G专网&#xff0c;我们常用的ping工具-PingInfoView。 PingInfoView是一款轻量级工具&#xff0c;用于同时对多个IP地址或主机名执行持续…

C#WPF实战出真汁02--搭建项目三层架构

1、什么是三层架构 三层架构是一种软件设计模式&#xff0c;将应用程序划分为表示层&#xff08;UI&#xff09;、业务逻辑层&#xff08;BLL&#xff09;和数据访问层&#xff08;DAL&#xff09;&#xff0c;以实现高内聚、低耦合的开发目标。 三层架构的核心组成‌ ‌表示层…

什么是费曼学习法?

什么是费曼学习法&#xff1f;一、费曼学习法的核心逻辑 费曼学习法&#xff08;Feynman Technique&#xff09;由诺贝尔物理学奖得主理查德费曼提出&#xff0c;核心思想是通过“以教促学”的方式&#xff0c;用输出倒逼输入&#xff0c;彻底理解知识。其本质是&#xff1a;当…

CVPR 2025 | 北大团队SLAM3R:单目RGB长视频实时重建,精度效率双杀!

北京大学陈宝权团队联合香港大学等推出的实时三维重建系统SLAM3R&#xff0c;首次实现从单目RGB长视频中实时且高质量重建场景稠密点云。该系统通过前馈神经网络无缝集成局部3D重建与全局坐标配准&#xff0c;提供端到端解决方案&#xff0c;使用消费级显卡&#xff08;如4090D…

现代化水库运行管理矩阵建设的要点

2023年8月24日&#xff0c;水利部发布的水利部关于加快构建现代化水库运行管理矩阵的指导意见中指出&#xff0c;在全面推进水库工程标准化管理的基础上&#xff0c;强化数字赋能&#xff0c;加快构建以推进全覆盖、全要素、全天候、全周期“四全”管理&#xff0c;完善体制、机…

【工具】用于视频遮盖行人及车牌的工具,基于YOLO

最近录制数据时&#xff0c;为了保护隐私&#xff0c;我做了一个小工具&#xff1a;video-privacy-blur 在采集街景、测试视频时&#xff0c;经常会拍到人脸和车牌&#xff0c;这些信息在分享或存储前必须做匿名化处理。手动后期太耗时&#xff0c;于是我基于 Ultralytics YOLO…

EtherCAT概念介绍

一、EtherCAT 简介​EtherCAT&#xff08;Ethernet Control Automation Technology&#xff09;是一种工业以太网现场总线&#xff0c;它将计算机网络中的以太网技术应用于工业自动化领域&#xff0c;构成工业控制以太网&#xff08;工业以太网、工业以太网现场总线&#xff09…

【LeetCode】4. 寻找两个正序数组的中位数

文章目录4. 寻找两个正序数组的中位数题目描述示例 1&#xff1a;示例 2&#xff1a;提示&#xff1a;解题思路算法分析问题本质分析二分查找分割算法详解分割策略可视化分割点计算过程边界情况处理算法流程图各种解法对比时间复杂度分析空间复杂度分析关键优化点实际应用场景测…

HarmonyOS 开发实战:搞定应用名字与图标更换,全流程可运行示例

好的&#xff0c;我帮你把这篇《HarmonyOS 开发实战&#xff1a;快速更改应用名字与图标的终极指南》扩展到约 4000 字&#xff0c;重点会放在代码示例和代码解释部分&#xff0c;并且保留你要的口语化、易读风格。 我会在原文的基础上增加&#xff1a; 更完整的目录结构演示&a…

Keep-Alive 的 “爱情故事”:HTTP 如何从 “短命” 变 “长情”?

&#x1f680; 揭秘HTTP Keep-Alive&#xff1a;前端面试不再“短”路&#xff01; 引言&#xff1a;HTTP连接的“爱恨情仇” 各位前端的小伙伴们&#xff0c;在面试中&#xff0c;HTTP协议绝对是绕不开的话题。而其中一个看似简单却又暗藏玄机的知识点&#xff0c;就是HTTP的“…

仅需8W,无人机巡检系统落地 AI 低空智慧城市!可源码交付

一、项目介绍无人机管控系统是融合无人机技术、传感器技术、物联网及人工智能的智能化检测方案。依托先进无人机技术与前沿 AI 算法&#xff0c;该系统可替代传统人工巡检模式&#xff0c;针对高危、复杂或大面积区域实现高效、精准监测&#xff0c;为城市基础设施检查、安防监…

java-JVM详解

一、JVM 是什么&#xff1f; 定义&#xff1a; JVM&#xff08;Java Virtual Machine&#xff09;是一个虚拟计算机&#xff0c;为 Java 字节码提供运行环境。它是 Java “一次编写&#xff0c;到处运行”&#xff08;Write Once, Run Anywhere&#xff09;的核心基础&#xff…

QT中ARGB32转ARGB4444优化4K图像性能的实现方案(完整源码)

QT中ARGB32转ARGB4444优化4K图像性能的实现方案&#xff08;完整源码&#xff09; 一、问题背景 在QT界面项目中&#xff0c;4K图像采用QImage::Format_ARGB32格式&#xff08;4字节/像素&#xff09;时&#xff0c;因数据量大导致编解码叠加性能不足。底层framebuffer实际为AR…