Flask-SQLAlchemy
参考学习了 阮一峰ORM 实例教程
面向对象编程把所有实体看成对象(object),关系型数据库则是采用实体之间的关系(relation)连接数据。ORM就是通过面向对象的思维去操作数据库,ORM 使用对象封装了数据库操作,因此可以不碰 SQL 语言。开发者只使用面向对象编程,与数据对象直接交互,不用关心底层数据库。
ORM 把数据库映射成对象:
- 数据库的表(table) --> 类(class)
- 记录(record,行数据)--> 对象(object)
- 字段(field)--> 对象的属性(attribute)
许多语言都有自己的 ORM 库,最典型、最规范的实现公认是 Ruby 语言的 Active Record。Active Record 对于对象和数据库表的映射,有一些命名限制。
(1)一个类对应一张表。类名是单数,且首字母大写;表名是复数,且全部是小写。比如,表books
对应类Book
。
(2)如果名字是不规则复数,则类名依照英语习惯命名,比如,表mice
对应类Mouse
,表people
对应类Person
。
(3)如果名字包含多个单词,那么类名使用首字母全部大写的骆驼拼写法,而表名使用下划线分隔的小写单词。比如,表book_clubs
对应类BookClub
,表line_items
对应类LineItem
。
(4)每个表都必须有一个主键字段,通常是叫做id
的整数字段。外键字段名约定为单数的表名 + 下划线 + id,比如item_id
表示该字段对应items
表的id
字段。
SQLAlchemy是一个强大的关系型数据库框架,提供了高层ORM,也提供了使用数据库原生SQL的功能,Flask-SQLAlchemy是一个Flask扩展,简化了SQLAlchemy的操作。
SQLAlchemy创建的数据库实例为模型提供了一个基类以及一系列的辅助类、辅助函数,可用于定义模型的数据结构。
一对多:
1、定义模型
比如:一个Role对应对应多个User, 一个User对应多个Post
#定义模型 class Role(db.Model): __tablename__ = 'roles' id = db.Column(db.INTEGER, primary_key=True) name = db.Column(db.String(64), unique=True) def __repr__(self): return self.name class User(db.Model): __tablename__ = 'users' id = db.Column(db.INTEGER, primary_key=True) username = db.Column(db.String(64), unique=True, index=True) def __repr__(self): return self.username
2、建立关系
class Role(db.Model): #...... # relationship给Role类增加了一个users属性,内容是以role.id为关联的一组User, # backref='role'给User类增加了一个role属性,内容是以role.id为关联的Role,可以代替role_id访问Role模型(通过u.role),否则只能用u.role_id
# 第一个参数是建立关系的表名称 users = db.relationship('User', backref='role') class User(db.Model): #...... role_id = db.Column(db.INTEGER, db.ForeignKey('roles.id')) #定义关系,在多的一侧加入一个外键关联,指向“一”这一侧的记录,对应到Role中的id,建立“一对多”的关系,
3、准备数据
def insert_data(): admin_role = Role(name='Admin') mod_role = Role(name='Mod') user_role = Role(name='User') #如果没有backref='role',则只能通过role_id=1来插入数据 user_join = User(username='join', role=admin_role) user_susan = User(username='susan', role_id=3) user_david = User(username='david', role=user_role) db.session.add_all([admin_role, mod_role, user_role, user_join, user_susan, user_david]) if __name__ == '__main__': insert_data() db.session.commit()
4、查询
for r in Role.query.all(): print('Role自身数据:{},{}'.format(r.id, r.name)) print('r.user:{}'.format(r.users))# relationship给Role类增加了一个users属性,内容是以role.id为关联的一组User for u in User.query.all(): print('User自身数据:{},{}'.format(u.id, u.username)) print('正向,外键foreignkey作用:role_id={},role={}'.format(u.role_id, u.role)) # backref='role'给User类增加了一个role属性,内容是以role.id为关联的Role。 print('反向,backfer字段:b.role.name={}'.format(u.role.name))
5、可以在视图函数中直接操作数据库
多对多:
多对多关系可以分解成原表和关联表之间的两个多对一的关系,通过一个关联表将两个表关联起来。
1、建立模型
# 关联表,第一个参数为中间表的表名,然后在表中添加两个字段,这两个字段分别为关联的两个表的一对多的外键,也同为主键
# 关联表就是一个简单的表,不是模型,SQLAlchemy会自动接收这个表
registrations =db.Table('registrations', db.Column('student_id', db.INTEGER, db.ForeignKey('students.id'), primary_key=True), db.Column('class_id', db.INTEGER, db.ForeignKey('classes.id'), primary_key=True)) class Student(db.Model): __tablename__ = 'students' id = db.Column(db.INTEGER, primary_key=True) name = db.Column(db.String(64)) # 多对多可以在任意一方中添加一个relationship字段,第一个参数为关联的另外一个表, # 第二个参数为中间表的表名,第三个参数backref是反向查询时的关键字(这里给Student增加了一个classes属性,给Class增加了一个students属性) classes = db.relationship('Class', secondary=registrations, backref='students', lazy='dynamic') def __repr__(self): return self.name class Class(db.Model): __tablename__ = 'classes' id = db.Column(db.INTEGER, primary_key=True) name = db.Column(db.String(64)) def __repr__(self): return self.name
2、准备数据
def insert_data2(): stu1 = Student(name='cc') stu2 = Student(name='dd') cur1 = Class(name="python") cur2 = Class(name="c") cur3 = Class(name="java") # 关联数据,给student添加classes属性 stu1.classes.append(cur1) stu1.classes.append(cur2) stu2.classes.append(cur2) stu2.classes.append(cur3) # 添加到数据库 db.session.add_all([stu1, stu2, cur1, cur2, cur3]) db.session.commit()
3、查询
# 通过student查询classes courses = Student.query.filter_by(name='dd').first().classes.all()#这里最后的all()是因为前面的lazy='dynamic' print('dd的courses:', courses) # 通过classes查询students stus = Class.query.filter_by(name='c').first().students print('c课程stuents:', stus)
自引用关系:
学生和课程的多对多关系中,是两个实例之间的多对多关系。
但是关注用户存在的多对多关系,只有用户一个实例。如果关系中的两侧都在同一个表中,这种关系成为自引用关系。
1、关联表的模型
# 多对多关系,关联表完全是由SQLALchemy掌控的内部表,为了能在关系中处理自定义的数据,提升关联表的地位,使用程序可访问的模型 # 要把这个多对多关系的左右两侧拆分成两个基本的一对多关系 class Follow(db.Model): __tablename__ = 'follows' # follower_id、followed_id外键关联的都是users.id,因为是只有一个实例的自引用关系 follower_id = db.Column(db.INTEGER, db.ForeignKey('users.id'), primary_key=True) followed_id = db.Column(db.INTEGER, db.ForeignKey('users.id'), primary_key=True) timestamp = db.Column(db.DATETIME, default=datetime.utcnow)
2、在User模型中增加外键关联,实现两个一对多的关系
# 用户模型 class User(UserMixin, db.Model): #... # 把这个多对多关系的左右两侧拆分成两个基本的一对多关系 # 建立一对多关系,给User增加一个followed属性(被关注的),给Follow增加一个follower属性(关注者) followed = db.relationship('Follow', foreign_keys=[Follow.follower_id],#为了消除外键之间的歧义,建立关系是使用foreign_keys指定的外键 backref=db.backref('follower', lazy='joined'),#回引Follow模型,joined实现立即从联结查询中加载相关对象 lazy='dynamic',#不会直接返回记录,而是返回查询对象,可在其上添加过滤器 cascade='all, delete-orphan')#配置在父对象上执行的操作对相关对象的影响 # 建立一对多关系,给User增加一个followers属性,给Follow增加一个followed属性 followers = db.relationship('Follow', foreign_keys=[Follow.followed_id],#为了消除外键之间的歧义,建立关系是使用foreign_keys指定的外键 backref=db.backref('followed', lazy='joined'),#回引Follow模型,joined实现立即从联结查询中加载相关对象 lazy='dynamic', cascade='all, delete-orphan')
3、增加数据
david = User.query.filter_by(username='david').first() #id=1 susan = User.query.filter_by(username='susan').first() #id=2 john = User.query.filter_by(username='john').first() #id=3 john.follow(david) susan.follow(john) susan.follow(david)
follows表如下:
follower_id | followed_id | timestamp |
1 | 3 | 2020-04-19 13:23:36 |
2 | 1 | 2020-04-19 13:23:39 |
2 | 3 | 2020-04-19 13:23:45 |
4、查询
john.is_following(david) #True david.is_followed_by(john) #True david.is_followed_by(susan) #True john.is_following(susan) #False john.followed.count()#1 susan.followed.count() #2 被susan关注的人 susan.followers.count() #0 关注susan的人 j = john.followed.first() #得到的是一个Follow模型 j.followed_id #3--------->followed:david j.follower_id #1--------->follower:john for i in susan.followed.all(): print(i.followed_id) #1 3 被susan关注的人 for j in david.followers.all(): print(j.follower_id) #1 2 关注daivd的人
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】