SQLAlchemy ORM-表与表之间的关系

发布于:2025-09-11 ⋅ 阅读:(16) ⋅ 点赞:(0)

1、什么是外键(Foreign Key)

外键是指一个表中的字段,它引用了另一个表中的主键字段。外键的主要作用是保证数据的完整性和关联性,确保一个表中的某个字段的值必须存在于另一个表的对应字段中。

外键约束的作用:

  • 数据完整性:防止插入无效的引用数据。
  • 关系映射:实现表与表之间的关联,方便通过 ORM 进行数据查询。

2、如何在 SQLAlchemy 模型中定义外键字段

在 SQLAlchemy 中,定义外键字段主要通过 ForeignKey 关键字参数实现。示例如下:

from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()


class Parent(Base):
    __tablename__ = 'parents'
    id = Column(Integer, primary_key=True)
    name = Column(String)

    children = relationship("Child", back_populates="parent")  # 定义关系属性


class Child(Base):
    __tablename__ = 'children'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    parent_id = Column(Integer, ForeignKey('parents.id'))  # 定义外键,关联父表的id字段

    parent = relationship("Parent", back_populates="children")  # 定义关系属性

在上述代码中Child类就是在其属性中定义了parent_id,并且通过ForeignKey()方法绑定了Parent表中的id(主键),Child和Parent类中的relationship定义了双向关系。

疑问1:children = relationship("Child", back_populates="parent")这串代码是什么含义?

首先children是Parent类的一个属性,

其次在relationship("Child", ...) 中的 "Child"表示的是这个关系连接的是Child这个类(对应的是这个类中的在数据库中的表,也就是children表),

最后的back_populates="parent"的作用是告诉数据库在Child类中有个属性叫parent,它和 Parent 类的 children 是“互相对应”的关系。也就是可以让你通过 parent.children 找到所有孩子,也可以通过 child.parent 找到对应的父亲。

疑问2:back_populates参数的作用是什么,可以不填吗?

1.什么是back_populates?

  • back_populates 告诉 SQLAlchemy:两个关系属性是“互相对应”的,属于同一对关系的两端。
  • 它让 ORM 知道,当你修改一端,另一端也会自动更新,保持数据一致。

2. 如果双方都不写 back_populates 会怎样?

假设代码是:

class Parent(Base):
    __tablename__ = 'parents'
    id = Column(Integer, primary_key=True)
    children = relationship("Child")  # 没有 back_populates

class Child(Base):
    __tablename__ = 'children'
    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey('parents.id'))
    parent = relationship("Parent")  # 也没有 back_populates

结果:

  • SQLAlchemy 会默认把它们当作两个独立的关系,它们之间没有明确的“对应”关系。
  • 你可以通过 parent.children 访问孩子列表,也可以通过 child.parent 访问父亲对象,但 ORM 不知道这两个属性是互相关联的两端。
  • 修改一端不会自动同步另一端

3.如果只写一边 back_populates 会怎样?

  • 只写一边会导致警告或错误,SQLAlchemy 需要两端都明确对应。
  • 推荐两边都写,保持对称。

4.对于back_populates的总结

  • back_populates 让 ORM 知道这两个属性是“互为镜像”,自动帮你管理关系的双向同步。
  • 它保证你操作一端时,另一端的属性也会自动更新,避免数据不一致。
  • 让代码更简洁,减少出错。

疑问3:插入数据时是否需要填写 children 或 parent 参数?

答案是不需要,在没有填写这些参数的时候,对应的children和parent都是空列表,可以创建好父类了之后再通过append方法添加孩子

3、一对多关系

什么是一对多关系?

一对多关系指的是一个表中的一条记录可以关联到另一个表中的多条记录。例如,一个“父亲”可以有多个“孩子”,但每个孩子只能有一个父亲。

如何实现一对多关系?

  • 在“多”的一方定义外键字段,指向“一”的一方的主键。
  • 在 ORM 中,使用 relationship 来定义双向访问。

示例:

class Parent(Base):
    __tablename__ = 'parents'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    children = relationship("Child", back_populates="parent")  # 父亲对应多个孩子

class Child(Base):
    __tablename__ = 'children'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    parent_id = Column(Integer, ForeignKey('parents.id'))
    parent = relationship("Parent", back_populates="children")  # 孩子对应一个父亲

通过这种方式,我们可以方便地通过 parent.children 访问所有孩子,通过 child.parent 访问对应的父亲。

4、多对多关系

什么是多对多关系?

多对多关系指的是两个表中的多条记录可以相互关联。例如,一个学生可以选修多门课程,一门课程也可以被多个学生选修。

如何实现多对多关系?

多对多关系需要借助一个关联表(中间表)来实现。关联表包含两个外键,分别指向两个主表的主键。

示例:

from sqlalchemy import Table

# 关联表
association_table = Table('association', Base.metadata,
    Column('student_id', Integer, ForeignKey('students.id')),
    Column('course_id', Integer, ForeignKey('courses.id'))
)

class Student(Base):
    __tablename__ = 'students'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    courses = relationship("Course", secondary=association_table, back_populates="students")

class Course(Base):
    __tablename__ = 'courses'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    students = relationship("Student", secondary=association_table, back_populates="courses")

说明:

  • association_table 是一个纯粹的关联表,不包含额外字段,只用于连接两个表。
  • secondary 参数指定了关联表。
  • back_populates 实现双向访问

疑问1:为什么多对多关系中的外键需要定义在第三方表中,可以像一对多关系一样定义在自己表中吗?

1.多对多不能用单表外键表达的原因

假设你想在学生表里放一个 course_id 外键,表示学生选修的课程。

  • 这意味着每个学生只能选修一门课程,因为一条学生记录只有一个 course_id 字段。
  • 但多对多要求的是:一个学生可以选修多门课程。
  • 所以,单个表里的外键字段无法存储多个课程ID。

同理,如果在课程表放学生ID,也只能表示一门课程对应一个学生,不符合多对多。

2.为什么第三方关联表解决了这个问题?

关联表(association table)有两列外键:

  • student_id 指向学生表
  • course_id 指向课程表

每行记录代表一个“学生-课程”的关联。

  • 一个学生可以有多条关联记录(多门课程)
  • 一门课程也可以有多条关联记录(多个学生)

这样,通过多条记录实现了“多对多”的映射关系

5、总结

外键: 是实现表与表之间关联的核心,保证数据完整性。

一对多关系: 通过在“多”方定义外键实现,使用 relationship 方便访问。

多对多关系: 需要中间关联表,通过 secondary 参数在 ORM 中实现。


网站公告

今日签到

点亮在社区的每一天
去签到