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
英文地址,請注明出處:http://en.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…