Flask – 模型的关联关系

一对多

学生模型:

1
2
3
4
5
6
7
class Student(db.Model):
__tablename__ = 'student'

s_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
s_name = db.Column(db.String(20), unique=True)
s_age = db.Column(db.Integer, default=18)
s_g = db.Column(db.Integer, db.ForeignKey('grade.g_id'), nullable=True)

班级模型:

1
2
3
4
5
6
7
8
class Grade(db.Model):
__tablename__ = 'grade'

g_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
g_name = db.Column(db.String(10), unique=True)
g_desc = db.Column(db.String(100), nullable=True)
g_time = db.Column(db.Date, default=datetime.now)
students = db.relationship('Student', backref='grade', lazy=True)

官网解释有如下几个lazy的参数:

lazy 决定了 SQLAlchemy 什么时候从数据库中加载数据:,有如下四个值:

select/True: (which is the default) means that SQLAlchemy will load the data as necessary in one go using a standard select statement.

joined/False: tells SQLAlchemy to load the relationship in the same query as the parent using a JOIN statement.

subquery: works like ‘joined’ but instead SQLAlchemy will use a subquery.

dynamic: is special and useful if you have many items. Instead of loading the items SQLAlchemy will return another query object which you can further refine before loading the items. This is usually what you want if you expect more than a handful of items for this relationship

1
2
3
select就是访问到属性的时候,就会全部加载该属性的数据。
joined则是在对关联的两个表进行join操作,从而获取到所有相关的对象。
dynamic则不一样,在访问属性的时候,并没有在内存中加载数据,而是返回一个query对象, 需要执行相应方法才可以获取对象,

通过班级查询学生信息:

1
2
3
4
5
6
7
8
9
10
@grade.route('/selectstubygrade/<int:id>/')
def select_stu_by_grade(id):
grade = Grade.query.get(id)
# 通过班级对象.定义的relationship变量去获取学生的信息
stus = grade.students

return render_template('grade_student.html',
stus=stus,
grade=grade
)

通过学生信息查询班级信息:

1
2
3
4
5
6
7
8
9
@stu.route('/selectgradebystu/<int:id>/')
def select_grade_by_stu(id):
stu = Student.query.get(id)
# 通过学生对象.定义的backref参数去获取班级的信息
grade = stu.grade

return render_template('student_grade.html',
grade=grade,
stu=stu)

注意:表的外键由db.ForeignKey指定,传入的参数是表的字段。db.relationship它声明的属性不作为表字段,第一个参数是关联类的名字,backref是一个反向身份的代理,相当于在Student类中添加了grade的属性。例如,有Grade实例dept和Student实例grade。dept.students.count()将会返回学院学生人数;stu.grade.first()将会返回学生的学院信息的Grade类实例。一般来讲db.relationship()会放在一这一边。

多对多

创建中间表

1
2
3
4
sc = db.Table('sc',
db.Column('s_id', db.Integer, db.ForeignKey('student.s_id'), primary_key=True),
db.Column('c_id', db.Integer, db.ForeignKey('courses.c_id'), primary_key=True)
)

创建课程表的模型,Course类

1
2
3
4
5
6
7
8
9
10
11
class Course(db.Model):
__tablename__ = 'courses'

c_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
c_name = db.Column(db.String(20), unique=True)
students = db.relationship('Student',
secondary=sc,
backref='cou')

def __init__(self, name):
self.c_name = name

sc表由db.Table声明,我们不需要关心这张表,因为这张表将会由SQLAlchemy接管,它唯一的作用是作为students表和courses表关联表,所以必须在db.relationship()中指出sencondary关联表参数。lazy是指查询时的惰性求值的方式,这里有详细的参数说明,而db.backref是声明反向身份代理,其中的lazy参数是指明反向查询的惰性求值方式。

添加学生和课程之间的关系

通过页面中传递学生的id和课程的id,分别获取学生的对象和课程的对象,在使用关联关系append去添加学生对象,并且add以后再commit后,就可以在中间表sc中查看到新增的关联关系了。

1
2
3
4
5
6
7
8
9
userid = request.form.get('userid')
courseid = request.form.get('courseid')

stu = Student.query.get(userid)
cou = Course.query.get(courseid)

cou.students.append(stu)
db.session.add(cou)
db.session.commit()

删除学生和课程之间的关系

通过页面获取传递的学生的id和课程的id,分别获取学生对象和课程对象,在使用关联关系remove去删除学生对象,并commit将事务提交到数据库中

1
2
3
4
5
stu = Student.query.get(s_id)
cou = Course.query.get(c_id)

cou.students.remove(stu)
db.session.commit()

通过课程查询学生的信息

以下定义在课程course的模型中,所以通过课程查询学生的信息,语法为课程的对象.students。如果知道学生的信息反过来找课程的信息,则使用backref的反向关联去查询,语法为学生的对象.cou(反向)

students = db.relationship(‘Student’,secondary=sc,backref=’cou’)

1
2
cou = Course.query.get(2)
stus = cou.students

通过学生去查询课程的信息

1
2
stu = Student.query.get(id)
cous = stu.cou