多表关系 - 一对多
简介
在 SQLAlchemy 中,一对多(One-to-Many)关系是一种常见的数据库关系模型,其中一个表的每个记录(行)对应于另一个表中多个记录。
实际上,建立多表关联关系的本质是要通过关联关系建立额外的信息查询。比如在 SQL 语句中经常要用到 join 进行关联查询,那么在 ORM 中就可以使用多表关系,使查询更加优雅。
一对多场景
在现实世界的数据模型中,经常会遇到一个实体与多个相关实体之间的关系。
使用一对多关系有助于更好地组织和存储数据,使数据模型更贴近实际情况。这种关系可以减少数据冗余,提高数据的一致性,并支持更高级的查询和分析。一对多关系在数据库设计和应用程序开发中是非常有用和常见的。
- 在组织中,部门通常包含多名员工。使用一对多关系,可以轻松地将员工与其所属部门关联起来。
- 在博客、新闻或论坛等内容管理系统中,一篇文章通常包含多个评论。使用一对多关系,可以将评论与其相关的文章连接起来。
- 学校可以包括多个班级,每个班级可以包含多名学生。通过一对多关系,可以管理学校、班级和学生的层次结构。
定义使用一对多关系
下面演示如何在 SQLAlchemy 中定义和使用一对多关系。
建立一对多关系
例如有两张表:一张是 ClassInfo(班级)
,另一个是 StudentInfo(学生)
。一个班级可以有多个学生,但每个学生只属于一个班级。这就是一对多关系。
一对多关系通常通过在"多"的一方的记录中引入外键来建立。这个外键指向"一"的一方的主键,这样就可以建立两者之间的关系。
以下是定义一对多关系的代码实现。
from sqlalchemy import *
from sqlalchemy.orm import declarative_base
Base = declarative_base()
# 班级表
class ClassInfo(Base):
"""班级"""
__tablename__ = "class_info"
# ID 编号
id = Column(Integer, primary_key=True)
# 班级名称
name = Column(String(30))
def __repr__(self):
return f"<Class id: {self.id}, name: {self.name}>"
# 学生表
class StudentInfo(Base):
"""学生"""
__tablename__ = 'student_info'
# ID编号
id = Column(Integer,primary_key=True)
# 名称
name = Column(String(30))
# 班级编号
class_id = Column(Integer, ForeignKey("class_info.id"))
def __repr__(self):
return f"<Student id: {self.id}, name: {self.name},
class_id: {self.class_id}>"
在这个例子中,StudentInfo 模型有一个名为 class_id 的属性,它通过 ForeignKey 定义了与 ClassInfo 模型的一对多关系。
接下来,使用这些模型进行操作。
创建表
首先,需要创建数据库连接,然后在数据库中创建表。
# 创建数据库连接
engine = create_engine('sqlite:///school.db')
DBSession = sessionmaker(bind=engine)
db_session: Session = DBSession()
if __name__ == '__main__':
# 创建表
Base.metadata.create_all(engine)
# 删除表
# Base.metadata.drop_all(engine)
添加数据
- 分别向学生表和班级表添加数据:
add_all()
将数据添加到 sessioncommit()
提交到数据库close()
关闭连接
# Class 班级表:添加两条数据
class1 = ClassInfo(id=1, name="测开21期")
class2 = ClassInfo(id=2, name="测开22期")
db_session.add_all([class1, class2])
db_session.commit()
# Student 学生表:添加四条数据
student1 = StudentInfo(id=1, name="学生一", class_id=1)
student2 = StudentInfo(id=2, name="学生二", class_id=1)
student3 = StudentInfo(id=3, name="学生三", class_id=2)
student4 = StudentInfo(id=4, name="学生四", class_id=2)
db_session.add_all([student1, student2])
db_session.add_all([student3, student4])
# 提交操作
db_session.commit()
# 关闭连接
db_session.close()
查询数据 - 多查一
在 SQLAlchemy 中,从多表关系的一侧查询对应的一表记录,通常被称为“多查一”(Many-to-One)查询。在一对多关系中,通常是从“多”的一方(即具有外键的表)查询与其关联的“一”的一方(即被引用的表)。
要实现多查一,首先需要为 StudentInfo 模型与 ClassInfo 模型构建关系。
StudentInfo 中名为 class_id 的属性已经定义好了外键。如果要这两张表想要进行关联查询,还需要通过 relationship 构建表之间的关联关系。
# 学生表
class StudentInfo(Base):
"""学生"""
__tablename__ = 'student_info'
# ID编号
id = Column(Integer,primary_key=True)
# 名称
name = Column(String(30))
# 班级编号
class_id = Column(Integer, ForeignKey("class_info.id"))
# 反射
class_info = relationship("ClassInfo", backref='student_info')
def __repr__(self):
return f"<Student id: {self.id}, name: {self.name},
class_id: {self.class_id}>"
relationship()
中第一个参数需要传入要关联的模型的类名,这样就可以通过 StudentInfo 表获取 ClassInfo 表中的信息。
然后可以使用 backref 来指定反射的关系。backref 的值是当前表的表名。
这样定义之后,就可以进行查询操作了。
下面演示如果从“多”(StudentInfo
)查找关联的“一”(ClassInfo
)。
# 通过 Student 查询 Class 对象
# 多查一:id 为 3 的学生 对应的班级 id 和 name
stu_info = db_session.query(StudentInfo).filter_by(id=3).first()
# 直接通过属性获取 class_id
print(stu_info.class_id)
# 反向关联之后,获取类对象的属性
print(stu_info.class_info.id)
print(stu_info.class_info.name)
查询数据 - 一查多
在 SQLAlchemy 中,一对多(One-to-Many)关系中的“一查多”操作是指从“一”的一侧查询关联的“多”的一侧。
# 一查多 id为1的班级里的所有学生
my_class = db_session.query(ClassInfo).filter_by(id=1).first()
print(my_class.student_info)
修改数据
- 先查询,再修改
- 修改之后要 commit()
# 由一改多 数据修改
# 班级-- 找到要修改的学生 -- 修改学生属性
my_class = db_session.query(ClassInfo).filter_by(id=1).first()
print(my_class.student_info[0].name)
my_class.student_info[0].name = "学生一修改名字"
db_session.commit()
db_session.close()
# 由多改一
# 学生 -- 找到对应的班级 -- 修改班级属性
stu = db_session.query(StudentInfo).filter_by(id=1).first()
print(stu.class_info.name)
stu.class_info.name = "测开21期修改1"
db_session.commit()
db_session.close()
删除数据
- 删除一个学生,不影响班级
- 删除一个班级下的所有学生
- 这样就实现了一对多的多表关系定义与操作。
# 删除用的很少,几乎不用,了解即可
# 1. 删除一个学生 ,正常删除,不影响class
stu = db_session.query(StudentInfo).filter_by(id=1).first()
db_session.delete(stu)
db_session.commit()
db_session.close()
# 删除一个班级下所有的学生
my_class = db_session.query(ClassInfo).filter_by(id=2).first()
print(my_class.stu_infos)
db_session.query(StudentInfo).filter_by(class_id =my_class.id).delete()
db_session.commit()
db_session.close()
总结
在本章节中,介绍了多表关系中一对多的相关概念和 SQLAlchemy 的一对多关系的使用。
- 一对多简介
- 一对多场景
- 定义一对多模型类
- 创建表
- 添加数据
- 查询数据 - 多查一
- 查询数据 - 一查多
- 修改数据
- 删除数据