Flask 数据库多对多关系

数据库使用关系建立记录之间的联系。其中,一对多关系是最常用的关系类型,它把一个记录和一组相关的记录联系在一起。实现这种关系时,要在“多”这一侧加入一个外键,指向“一”这一侧联接的记录。大部分的其他关系类型都可以从一对多类型中衍生。 多对一关系从“多”这一侧看,就是一对多关系。 一对一关系类型是简化版的一对多关系, 限制“多”这一侧最多只能有一个记录。唯一不能从一对多关系中简单演化出来的类型是多对多关系,这种关系的两侧都有多个记录。

多对多关系

一对多关系、多对一关系和一对一关系至少都有一侧是单个实体,所以记录之间的联系通过外键实现,让外键指向这个实体。但是,你要如何实现两侧都是“多”的关系呢?

下面以一个典型的多对多关系为例,即一个记录学生和他们所选课程的数据库。很显然,你不能在学生表中加入一个指向课程的外键,因为一个学生可以选择多个课程,一个外键不够用。同样,你也不能在课程表中加入一个指向学生的外键,因为一个课程有多个学生选择。两侧都需要一组外键。这种问题的解决方法是添加第三张表, 这个表称为关联表。现在,多对多关系可以分解成原表和关联表之间的两个一对多关系。

下图 描绘了学生和课程之间的多对多关系。

这个例子中的关联表是 registrations,表中的每一行都表示一个学生注册的一个课程。查询多对多关系要分成两步。 若想知道某位学生选择了哪些课程,你要先从学生和注册之间的一对多关系开始, 获取这位学生在 registrations 表中的所有记录,然后再按照多到一的方向遍历课程和注册之间的一对多关系, 找到这位学生在 registrations 表中各记录所对应的课程。
同样,若想找到选择了某门课程的所有学生,你要先从课程表中开始,获取其在 registrations 表中的记录,再获取这些记录联接的学生。通过遍历两个关系来获取查询结果的做法听起来有难度, 不过像前例这种简单关系,SQLAlchemy 就可以完成大部分操作。

上图中的多对多关系使用的代码表示如下:

registrations = db.Table('registrations',
    db.Column('student_id', db.Integer, db.ForeignKey('students.id')),
    db.Column('class_id', db.Integer, db.ForeignKey('classes.id'))
)

class Student(db.Model):
    id = db.Column(db.Integer, primary_key=True)
   name = db.Column(db.String)
   classes = db.relationship('Class',secondary=registrations,
                                    backref=db.backref('students', lazy='dynamic'),
                                    lazy='dynamic')

class Class(db.Model):
    id = db.Column(db.Integer, primary_key = True)
    name = db.Column(db.String)

多对多关系仍使用定义一对多关系的 db.relationship() 方法进行定义,但在多对多关系中,必须把 secondary 参数设为关联表。多对多关系可以在任何一个类中定义, backref 参数会处理好关系的另一侧。关联表就是一个简单的表,不是模型,SQLAlchemy 会自动接管这个表。

这样处理多对多关系特别简单。假设学生是 s,课程是 c,学生注册课程的代码为:

>>> s.classes.append(c)

>>> db.session.add(s)

列出学生 s 注册的课程以及注册了课程 c 的学生也很简单:

>>> s.classes.all()

>>> c.students.all()

Class 模型中的 students 关系由参数 db.backref() 定义。注意,这个关系中还指定了 lazy= ‘dynamic‘ 参数,所以关系两侧返回的查询都可接受额外的过滤器。

如果后来学生 s 决定不选课程 c 了,那么可使用下面的代码更新数据库:

>>> s.classes.remove(c)

下面来看一个实际的例子:因为在设计中学生会转学院,所以,学生与学院是多对多的关系

1.定义模型

class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(100), unique=True, index=True)

    .............省略其他字段
    departments=db.relationship('Department', secondary=user_department, backref=db.backref('users',lazy='dynamic'), lazy='dynamic')
    
class Department(db.Model):
    __tablename__ = 'departments'
    id = db.Column(db.Integer, primary_key=True)
    department = db.Column(db.String(100))
user_department = db.Table('user_department',
    db.Column('user_id', db.Integer, db.ForeignKey('users.id'), primary_key=True),
    db.Column('department_id', db.Integer, db.ForeignKey('departments.id'), primary_key=True)
)

2.定义表单:

class SmForm(Form):
    name = StringField('真实姓名', validators=[Length(0, 64)])
    ....................省略其他字段
    is_departmentChange = BooleanField('是否转过学院')
    pre_department = SelectField('原学院:', coerce=int)
    cut_department = SelectField('现学院:', coerce=int)

    submit = SubmitField('Submit')

   #下拉菜单初始化
    def __init__(self, user, *args, **kwargs):
        super(SmForm, self).__init__(*args, **kwargs)
        <strong>self.pre_department.choices = [(pre_department.id, pre_department.department)
                             for pre_department in Department.query.order_by(Department.department).all()]
        self.cut_department.choices = [(cut_department.id, cut_department.department)
                             for cut_department in Department.query.order_by(Department.department).all()]</strong>

        self.user = user

3.定义路由:

@main.route('/sm', methods=['GET', 'POST'])
@login_required
@main.errorhandler(404)
def sm():
    user = User.query.filter_by(email=current_user.email).first()
    form = SmForm(user)
    if  user.is_realname ==False:
        if form.validate_on_submit():

            # User的学院更新  删除旧的数据,<strong>联合删除
            usr = current_user._get_current_object()
            deparment = user.departments.all()
            for de in deparment:
                de.users.remove(usr)</strong>

       ........................省略其他

            user.is_departmentChange = form.is_departmentChange.data
          <strong>#向关系表中添加
            user.departments.append(Department.query.get(form.pre_department.data))
            user.departments.append(Department.query.get(form.cut_department.data))

            db.session.add(user)
            db.session.commit()</strong>
            return redirect(url_for('.sm_success'))
    return render_template('sm.html', form=form)

4.渲染模板(省略)

时间: 2025-01-09 07:40:15

Flask 数据库多对多关系的相关文章

Flask 数据库多对多自引用关系

上篇介绍的多对多关系是两个模型是之间的多对多关系,关联表联接的是两个明确的实体,还有些情况下只有一个模型,与自己之间存在多对多关系.比如用户之间的关注.表示用户关注其他用户时,只有用户一个实体,没有 第二个实体.如果关系中的两侧都在同一个表中, 这种关系称为自引用关系. 在关注中, 关系的左侧是用户实体,可以称为"关注者":关系的右侧也是用户实体,但这些是"被关注者".从概 念上来看,自引用关系和普通关系没什么区别,只是不易理解.下图 是自引用关系的数据库图解,表示

Mysql数据库多对多关系未建新表

原则上,多对多关系是要新建一个关系表的,当遇到没有新建表的情况下如何查询多对多的SQL呢? FIND_IN_SET(str,strlist) 官网:http://dev.mysql.com/doc/refman/5.7/en/string-functions.html Mysql数据库 此数据库为设计不合理的demo,仅用来示范多对多关系. 创建表结构 -- 新建user表 CREATE TABLE `user` ( `id` INT(11) NOT NULL COMMENT '人员id', `

数据库联合外键做主键(解决多对多关系)

多对多概念:一个学生可以学好几门课程,一门课程可以被好几个学生学习,站在某一个学生的角度就对应了多个课程,站在课程那里,就对应了多个学生,所以学生和课程就是一个简单的多对多关系. 在数据库中我们要解决这种多对多关系的应用,首先创建一个学生表,如 create table stu ( stu_id int primary key, sty_name varchar(20) ); 然后再创建一个课程表 create table book( book_id int primary key, book_

总结一下数据库的 一对多、多对一、一对一、多对多 关系

关联映射:一对多/多对一 存在最普遍的映射关系,简单来讲就如球员与球队的关系: 一对多:从球队角度来说一个球队拥有多个球员 即为一对多 多对一:从球员角度来说多个球员属于一个球队 即为多对一 数据表间一对多关系如下图: 关联映射:一对一 一对一关系就如球队与球队所在地址之间的关系,一支球队仅有一个地址,而一个地址区也仅有一支球队. 数据表间一对一关系的表现有两种,一种是外键关联,一种是主键关联.图示如下: 一对一外键关联: 一对一主键关联:要求两个表的主键必须完全一致,通过两个表的主键建立关联关

数据库中根据数据一对一,一对多,多对多关系设计

做一个项目,必然是少不了数据库设计的!在学习阶段,基本都是单表.然而在实际开发过程中,一对多,多对多的表处处都是!简单整理一下,一对多,多对多表如何设计整理一下思路: 数据库实体间有三种对应关系:一对一,一对多,多对多. 一对一关系示例: 一个学生对应一个学生档案材料,或者每个人都有唯一的身份证编号. 一对多关系示例: 一个学生只属于一个班,但是一个班级有多名学生. 多对多关系示例: 一个学生可以选择多门课,一门课也有多名学生. 1.一对多关系处理: 通过学生和班级问题了解一对多: 设计数据库表

数据库中多对多关系及其实现

事物A中的一条记录可以对应事物B中的多条记录,而事物B中的一条记录也可以对应事物A中的多条记录.例如:学校中,一个班级可以有多名任课老师,而一名老师可以教多个班级.这时,可以引入一个表C来模拟多对多关系. SQL语句: --班级表 create table banji ( banji_id int primary key, banji_num int not null, banji_name nvarchar(100) ) --教师 create table jiaoshi ( jiaoshi_

数据库的 一对多、多对一、一对一、多对多 关系

数据库实体间有三种对应关系:一对一,一对多,多对多.一对一关系示例:一个学生对应一个学生档案材料,或者每个人都有唯一的身份证编号.一对多关系示例:一个学生只属于一个班,但是一个学院有多名学生.多对多关系示例:一个学生可以选择多门课,一门课也有多名学生.这三种关系在数据库中逻辑结构处理分析:1.一对多关系处理:我们以学生和班级之间的关系来说明一对多的关系处理方法.假设现有基本表学生表(学号,姓名,--),班级表(班级号,备注信息,--). 方法一:新增一个关系表,用来表示学生与班级的属于关系,该关

数据库一对一,一对多,多对多关系

关联映射:一对多/多对一 存在最普遍的映射关系,简单来讲就如球员与球队的关系: 一对多:从球队角度来说一个球队拥有多个球员 即为一对多 多对一:从球员角度来说多个球员属于一个球队 即为多对一 数据表间一对多关系如下图: 关联映射:一对一 一对一关系就如球队与球队所在地址之间的关系,一支球队仅有一个地址,而一个地址区也仅有一支球队. 数据表间一对一关系的表现有两种,一种是外键关联,一种是主键关联.图示如下: 一对一外键关联: 一对一主键关联:要求两个表的主键必须完全一致,通过两个表的主键建立关联关

逻辑数据库设计 - 乱穿马路(多对多关系)

一.乱穿马路模式介绍 程序员通常使用逗号分隔的列表来避免在多对多关系中创建交叉表,这种设计方式定义为一种反模式,称为乱穿马路. 例如:在一个产品管理系统中,一个人可以有多个产品,一个产品必须对应一个人,因此有如下数据库: 但是,随着时间的推移,出现了一个产品可能会有多个联系人.于是为了最小限度地修改数据库,可能不少人会将Account_Id的类型修改成varchar,这样就可以列出该列中的多个账号Id,每个Id之间用逗号分隔.这样的设计貌似可行,因为并没有创建额外的表或者列.仅仅改变了一个字段的