一. 一对多关系
我们前面讲了两个一对多关系:角色和它所对应的用户, 用户和它所对应的文章, 在一对多关系中, 我们在‘一’的这边设置db.relationship, 在‘多’的一边设置一个外键。
二. 多对多关系
而数据库除了一对多关系之外还有多对多关系。最常见的是学生选课这个多对多关系, 一个学生可以选择多门课程, 一门课程也可以被多个学生选择, 如果我们在学生这边添加外键显然行不通, 因为一个学生可以选择多门课程, 在课程这边添加外键同理也行不通。那我们如何解决?
我们可以建立一张关系表, 用两个一对多关系表示多对多关系:
一 ————————》 多 《——————— 一
一个学生对应多个选课记录, 从而查找到这个学生选择的多门课程;
一门课程也对应多个选课记录, 从而查找到选这门课程的多个学生。
三. 自引用多对多关系
1.概念:
用户之间的相互关注就是一个多对多关系, 关注者可以关注多个被关注者, 被关注者也可以被多个关注者关注, 这里的关注者和被关注者都是用户, 两端是同一个实体, 不像学生选课, 两端是不同实体。 这就是自引用多对多关系。
2. 实现:
class Follow(db.Model):
__tablename__ = 'follows'
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.Datetime, default=datetime.utcnow)
class User(UserMixin, db.Model):
followed = db.relationship('Follow',
foreign_keys=[Follow.follower_id],
backref=db.backref('follower', lazy='joined'),
cascade='all, delete-orphan')
followers = db.relationship('Follow',
foreign_keys=[Follow.followed_id],
backref=db.backref('followed', lazy='joined'),
cascade='all, delete-orphan')
3. 代码解释
上述代码使用两个一对多关系表示了多对多关系。
我们先回想一下用户和文章这个一对多关系,以便于我们理解用户之间的多对多关系:
class User(db.Model):
posts = db.relationship('Post', backref='author', lazy='dynamic')
class Post(db.Model):
author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
我们在‘一’这边的user模型中添加了一个posts属性, 用户实例调用posts会得到该用户的所有文章组成的列表, 并且用backref参数为post实例添加了author属性, 文章实例调用author属性时会得到它对应的用户。
我们在‘多’这边的Post模型中添加了author_id外键。
下面来讲解用户之间的多对多关系:
假设当前用户是关注者, 我们要在用户模型中添加一个属性followed, 用户调用followed会得到他所有的被关注者, 只不过这里是Follow实例组成的列表, 然后我们为Follow实例添加一个follower属性, Follow实例调用follower属性时得到关注者实例。并在‘多’这边添加follower_id外键。
假设当前用户是被关注者, 我们要在用户模型中添加一个属性followers, 用户调用该属性会的到他所有关注者, 只不过这里是Follow实例组成的列表, 然后我们为这些Follow实例添加一个followed属性, 调用得到被关注者实例。 并在‘多’这边添加followed_id外键。
backref=''joined'作用是减少数据库查询的次数。
cascade=‘all, delete-orphan’参数的作用是当删除用户时, 删除其对应的Follow实例, 而不是只把Follow记录中与其相关的外键设为空。
4.关注关系的辅助方法
class User(db.Model):
def follow(self, user): #关注user
if not self.is_following(user):
f = Follow(follower=self, followed=user)
db.session.add(f)
def unfollow(self, user): #取关user
f = self.followed.filter_by(followed_id=user.id).first()
if f:
db.session.delete(f)
def is_following(self, user): #判断是否关注user
return self.followed.filter_by(followed_id==user.id).first
def is_followed_by(self, user): #判断是否被user关注
return self.followers.filter_by(follower_id=user.id).first()