SQLAlchemy中如何理解‘自关联多对多’模型

本文介绍使用 Flask 和 SQLAlchemy 实现用户关注关系的多对多自关联模型。通过定义用户表和用户关系表,实现用户间的关注与被关注功能。代码展示了如何创建模型、添加数据及自关联关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

from flask_sqlalchemy import SQLAlchemy

from flask import Flask



app = Flask(__name__)

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:ccc@127.0.0.1:3306/many2many'

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

app.config['SQLALCHEMY_ECHO'] = False



db = SQLAlchemy(app)

tb_user_follows = db.Table(

    'tb_user_follows',

    db.Column('follower_id', db.Integer, db.ForeignKey('user.id'), primary_key=True),

    db.Column('followed_id', db.Integer, db.ForeignKey('user.id'), primary_key=True)

)



class User(db.Model):

    id = db.Column(db.Integer, primary_key=True)

    name = db.Column(db.String(32), unique=True, nullable=False)

    # 用户所有的粉丝,添加了反向引用followed,代表用户都关注了哪些人;加个括号会比较容易理解,同理secondaryjoin也是这样

    followers = db.relationship('User', secondary=tb_user_follows,

                                primaryjoin=(id == tb_user_follows.c.followed_id),

                                secondaryjoin=id == tb_user_follows.c.follower_id,

                                backref = db.backref('followed', lazy='dynamic'),

                                lazy= 'dynamic'

                                )



if __name__ == '__main__':

    db.drop_all()

    db.create_all()

    u1 = User(name='cc')

    u2 = User(name='ccdefans')

    u3 = User(name='superstar')

    u4 = User(name='ccdefans2')



    u1.followers = [u2, u4]

    u3.followers = [u1, u2, u4]



    db.session.add_all([u1, u2, u3, u4])

    db.session.commit()



    db.session.commit()

    app.run(debug=True)

添加数据后,查看两张表单,即可看到一张存储的是用户,一张存储的是用户关系表。

用户关系的字段顺序可以更改,但其指向的引用是自关联多对多的索引

 

这个是数据表

 

这个是多对多自关联关系表,可以很清晰地看出字段是如何引用的

<think>我们正在讨论SQLAlchemyORM中的多对多关系。多对多关系是指两个实体之间存在多对多的关联,例如一个学生可以选择多门课程,而一门课程也可以被多个学生选择。在SQLAlchemy中,多对多关系通常通过一个关联(associationtable)来实现,这个包含两个外键,分别指向两个相关的主键。在ORM中定义多对多关系,我们需要使用`relationship`函数,并通过`secondary`参数指定关联。下面是一个示例,展示如何定义多对多关系:1.首先,我们定义两个模型类(例如`Student`和`Course`)和一个关联(例如`student_course_association`)。2.关联通常使用`Table`类来定义,它包含两个外键列。3.在每个模型类中,我们使用`relationship`函数,并在其中通过`secondary`参数指定关联。具体步骤如下:###步骤1:定义关联关联是一个普通的SQLAlchemy,不映射到类(即不是一个模型)。它至少包含两个外键列,分别指向两个相关的主键。```pythonfromsqlalchemyimportTable,Column,Integer,ForeignKeyfromsqlalchemy.ormimportrelationshipfromsqlalchemy.ext.declarativeimportdeclarative_baseBase=declarative_base()#定义关联student_course_association=Table('student_course',Base.metadata,Column('student_id',Integer,ForeignKey('student.id')),Column('course_id',Integer,ForeignKey('course.id')))```###步骤2:定义模型类在`Student`类中,我们定义到`Course`类的多对多关系,使用`relationship`并设置`secondary`参数为上面定义的关联。同样,在`Course`类中,我们也可以定义到`Student`类的反向关系。```pythonclassStudent(Base):__tablename__='student'id=Column(Integer,primary_key=True)name=Column(String)courses=relationship("Course",secondary=student_course_association,back_populates="students")classCourse(Base):__tablename__='course'id=Column(Integer,primary_key=True)name=Column(String)students=relationship("Student",secondary=student_course_association,back_populates="courses")```注意:`back_populates`参数用于建立双向关系,这样两个属性可以相互同步。也可以使用`backref`参数,但`back_populates`更明确。###使用多对多关系现在我们可以操作多对多关系了:```python#创建学生和课程student1=Student(name="Alice")student2=Student(name="Bob")course1=Course(name="Math")course2=Course(name="Science")#将学生与课程关联student1.courses.append(course1)student1.courses.append(course2)student2.courses.append(course1)#提交到数据库session.add_all([student1,student2,course1,course2])session.commit()```###查询查询一个学生选择的所有课程:```pythonalice=session.query(Student).filter_by(name="Alice").first()forcourseinalice.courses:print(course.name)```查询选择某门课程的所有学生:```pythonmath=session.query(Course).filter_by(name="Math").first()forstudentinmath.students:print(student.name)```###关联有额外字段如果关联除了两个外键之外还有其他字段(例如添加一个“选课时间”字段),那么我们就不能使用简单的`Table`定义关联,而是需要创建一个关联模型类(也称为关联对象模式)。例如,我们创建一个`StudentCourse`类来示关联,并添加一个`enrollment_date`字段:```pythonclassStudentCourse(Base):__tablename__='student_course'student_id=Column(Integer,ForeignKey('student.id'),primary_key=True)course_id=Column(Integer,ForeignKey('course.id'),primary_key=True)enrollment_date=Column(DateTime,default=datetime.now)#定义与Student和Course的关系student=relationship("Student",back_populates="course_associations")course=relationship("Course",back_populates="student_associations")classStudent(Base):__tablename__='student'id=Column(Integer,primary_key=True)name=Column(String)course_associations=relationship("StudentCourse",back_populates="student")courses=association_proxy("course_associations","course",creator=lambdacourse:StudentCourse(course=course))classCourse(Base):__tablename__='course'id=Column(Integer,primary_key=True)name=Column(String)student_associations=relationship("StudentCourse",back_populates="course")students=association_proxy("student_associations","student",creator=lambdastudent:StudentCourse(student=student))```这样,在添加学生和课程关联时,我们可以设置额外的字段:```pythonstudent1.courses.append(course1)#通过association_proxy,会自动创建StudentCourse对象#或者显式创建sc=StudentCourse(student=student1,course=course2,enrollment_date=datetime(2021,9,1))session.add(sc)```在查询时,我们可以通过关联对象来访问额外字段。###总结在SQLAlchemy中实现多对多关系,通常有两种方式:1.使用关联(`Table`)并作为`relationship`的`secondary`参数,适用于关联只有两个外键列的情况。2.使用关联模型类,适用于关联有额外字段的情况,此时需要使用`association_proxy`来简化操作。以上就是在SQLAlchemyORM中使用多对多关系的基本方法。§§相关问题§§1.SQLAlchemy多对多关系的关联如何设计?2.当关联需要额外字段时,应该如何实现多对多关系?3.如何查询多对多关系中的数据?4.SQLAlchemy中的`back_populates`和`backref`有什么区别?5.在多对多关系中,如何实现级联删除?</think>### SQLAlchemy ORM 多对多关系实现与使用 在 SQLAlchemy ORM 中,多对多关系通过**关联(association table)**实现,该存储两个实体之间的关联关系。以下是详细实现步骤: #### 1. 基础实现(无额外字段) ```python from sqlalchemy import Table, Column, Integer, String, ForeignKey from sqlalchemy.orm import declarative_base, relationship Base = declarative_base() # 定义关联(不映射为ORM类) association_table = Table( 'student_course', 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(50)) courses = relationship( "Course", secondary=association_table, # 指定关联 back_populates="students" # 双向关系 ) class Course(Base): __tablename__ = 'courses' id = Column(Integer, primary_key=True) title = Column(String(100)) students = relationship( "Student", secondary=association_table, back_populates="courses" ) ``` #### 2. 关联对象模式(含额外字段) 当关联需要存储额外信息(如加入时间、成绩等)时: ```python class Enrollment(Base): __tablename__ = 'enrollments' student_id = Column(Integer, ForeignKey('students.id'), primary_key=True) course_id = Column(Integer, ForeignKey('courses.id'), primary_key=True) enrolled_date = Column(DateTime, default=datetime.now) grade = Column(String(2)) # 定义与主的双向关系 student = relationship("Student", back_populates="enrollments") course = relationship("Course", back_populates="enrollments") class Student(Base): __tablename__ = 'students' id = Column(Integer, primary_key=True) name = Column(String(50)) enrollments = relationship("Enrollment", back_populates="student") # 通过关联代理简化访问 courses = association_proxy( 'enrollments', 'course', creator=lambda course: Enrollment(course=course) ) class Course(Base): __tablename__ = 'courses' id = Column(Integer, primary_key=True) title = Column(String(100)) enrollments = relationship("Enrollment", back_populates="course") students = association_proxy( 'enrollments', 'student', creator=lambda student: Enrollment(student=student) ) ``` #### 3. 核心操作示例 ```python # 创建关系 math = Course(title="Mathematics") alice = Student(name="Alice") alice.courses.append(math) # 自动创建Enrollment记录 # 查询操作 # 获取Alice的所有课程 alice_courses = session.query(Student).filter_by(name="Alice").first().courses # 获取选修Math的学生 math_students = session.query(Course).filter_by(title="Mathematics").first().students # 带条件查询(使用关联对象模式) query = session.query(Enrollment).filter_by(grade="A+").join(Student) honor_students = [enrollment.student for enrollment in query] # 删除关系 alice.courses.remove(math) # 自动删除Enrollment记录 ``` #### 4. 最佳实践 1. **双向关系**:始终使用`back_populates`或`backref`保持双向同步 2. **关联代理**:使用`association_proxy`简化直接访问(如`student.courses`) 3. **级联删除**:通过`cascade="all, delete-orphan"`确保关联数据一致性 4. **查询优化**:`contains_eager()`预加载关联数据避免N+1查询问题[^1] 多对多关系是SQLAlchemy ORM的核心功能之一,最新版本2.0+对关系加载机制进行了优化,支持更灵活的`selectinload()`等加载策略[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值