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

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

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

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

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

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

  1. registrations = db.Table('registrations',
  2. db.Column('student_id', db.Integer, db.ForeignKey('students.id')),
  3. db.Column('class_id', db.Integer, db.ForeignKey('classes.id'))
  4. )
  5. class Student(db.Model):
  6. id = db.Column(db.Integer, primary_key=True)
  7. name = db.Column(db.String)
  8. classes = db.relationship('Class',secondary=registrations,
  9. backref=db.backref('students', lazy='dynamic'),
  10. lazy='dynamic')
  11. class Class(db.Model):
  12. id = db.Column(db.Integer, primary_key = True)
  13. 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.定义模型

  1. class User(UserMixin, db.Model):
  2. __tablename__ = 'users'
  3. id = db.Column(db.Integer, primary_key=True)
  4. email = db.Column(db.String(100), unique=True, index=True)
  5. .............省略其他字段
  6. departments=db.relationship('Department', secondary=user_department, backref=db.backref('users',lazy='dynamic'), lazy='dynamic')
  1. class Department(db.Model):
  2. __tablename__ = 'departments'
  3. id = db.Column(db.Integer, primary_key=True)
  4. department = db.Column(db.String(100))
  1. user_department = db.Table('user_department',
  2. db.Column('user_id', db.Integer, db.ForeignKey('users.id'), primary_key=True),
  3. db.Column('department_id', db.Integer, db.ForeignKey('departments.id'), primary_key=True)
  4. )

2.定义表单:

  1. class SmForm(Form):
  2. name = StringField('真实姓名', validators=[Length(0, 64)])
  3. ....................省略其他字段
  4. is_departmentChange = BooleanField('是否转过学院')
  5. pre_department = SelectField('原学院:', coerce=int)
  6. cut_department = SelectField('现学院:', coerce=int)
  7. submit = SubmitField('Submit')
  8. #下拉菜单初始化
  9. def __init__(self, user, *args, **kwargs):
  10. super(SmForm, self).__init__(*args, **kwargs)
  11. <strong>self.pre_department.choices = [(pre_department.id, pre_department.department)
  12. for pre_department in Department.query.order_by(Department.department).all()]
  13. self.cut_department.choices = [(cut_department.id, cut_department.department)
  14. for cut_department in Department.query.order_by(Department.department).all()]</strong>
  15. self.user = user

3.定义路由:

  1. @main.route('/sm', methods=['GET', 'POST'])
  2. @login_required
  3. @main.errorhandler(404)
  4. def sm():
  5. user = User.query.filter_by(email=current_user.email).first()
  6. form = SmForm(user)
  7. if  user.is_realname ==False:
  8. if form.validate_on_submit():
  9. # User的学院更新  删除旧的数据,<strong>联合删除
  10. usr = current_user._get_current_object()
  11. deparment = user.departments.all()
  12. for de in deparment:
  13. de.users.remove(usr)</strong>
  14. ........................省略其他
  15. user.is_departmentChange = form.is_departmentChange.data
  16. <strong>#向关系表中添加
  17. user.departments.append(Department.query.get(form.pre_department.data))
  18. user.departments.append(Department.query.get(form.cut_department.data))
  19. db.session.add(user)
  20. db.session.commit()</strong>
  21. return redirect(url_for('.sm_success'))
  22. return render_template('sm.html', form=form)

4.渲染模板(省略)

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

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

    原则上,多对多关系是要新建一个关系表的,当遇到没有新建表的情况下如何查询多对多的SQL呢? FIND_IN_SET(str,strlist) 官网:http://dev.mysql.com/doc/r ...

  2. Linux下开发python django程序(django数据库多对多关系)

    1.多对多关系数据访问 models.py设置 from django.db import models # Create your models here. sex_choices=( ('f',' ...

  3. MySQL基础9-主键约束、外键约束、等值连接查询、一对一和多对多关系

    1.主键约束和外键约束 外键约束 * 外键必须是另一表的主键的值(外键要引用主键!) * 外键可以重复 * 外键可以为空 * 一张表中可以有多个外键! 概念模型在数据库中成为表 数据库表中的多对一关系 ...

  4. 数据库表间多对多关系(附带额外字段)的实体类(POJO 或 POCO)表示

    介绍 在之前的 Entity Framework 快速上手介绍 之中,两个实体之间只是简单的一对一关系,而在实际的应用场景中,还会出现多对多关系,同时还有可能会出现多对多关系还附带有其他字段的情况. ...

  5. python3 + flask + sqlalchemy +orm(3):多对多关系

    一篇文章有多个tag,一个tag也可以属于多篇文章,文章和tag存在多对多关系 config.py DEBUG = True #dialect+driver://root:1q2w3e4r5t@127 ...

  6. Flask数据库常见关系模板代码

    常见关系模板代码 以下罗列了使用关系型数据库中常见关系定义模板代码 一对多 示例场景: 用户与其发布的帖子(用户表与帖子表) 角色与所属于该角色的用户(角色表与多用户表) 示例代码 class Rol ...

  7. MySQL数据库 crud语句 ifnull() 创建新账户 备份数据库 一对多关系 多对多(中间表) 外键约束 自关联 子查询注意事项 DML DDL DQL mysql面试题 truncate与delete的区别

    DML(data manipulation language): 它们是SELECT.UPDATE.INSERT.DELETE,就象它的名字一样,这4条命令是用来对数据库里的数据进行操作的语言 DDL ...

  8. EF Core中如何设置数据库表自己与自己的多对多关系

    本文的代码基于.NET Core 3.0和EF Core 3.0 有时候在数据库设计中,一个表自己会和自己是多对多关系. 在SQL Server数据库中,现在我们有Person表,代表一个人,建表语句 ...

  9. jango 模型管理数据model,数据库外键主键与一对一,一对多,多对多关系

    四.models.py 定义和管理模型: 4.1模型class的属性就映射与数据库的字段参数 继承models.Model class TestClass(models.Model): 4.2在数据库 ...

随机推荐

  1. 【第13届景驰-埃森哲杯广东工业大学ACM程序设计大赛-L】用来作弊的药水

    链接:https://www.nowcoder.com/acm/contest/90/L来源:牛客网 输入x,a,y,b,(1<=x,a,y,b<=10^9)判断x^a是否等于y^b 前面 ...

  2. shell编程学习1

    1.shell是操作系统的终端命令行 (1)shell可以理解为软件系统提供给用户操作的命令行界面,可以说它是人机交互的一种方式.    (2)我们可以使用shell和操作系统.uboot等软件系统进 ...

  3. python基础之多线程与多进程(一)

    并发编程? 1.为什么要有操作系统? 操作系统,位于底层硬件与应用软件之间 工作方式:向下管理硬件,向上提供接口 2.多道技术? 不断切换程序. 操作系统进程切换: 1.出现IO操作 2.固定时间 进 ...

  4. hp g6 2328tx 加装ssd 机械硬盘安装到光驱位置 问题小结

    惠普 g6 笔记本从13年入手至今,三年有余,性能啥的开始跟不上了,所有入手了一块ssd,闪迪(sanDisk)加强版 240G的固态硬盘,准备升下级.狗东下午下单,第二天早上7点多到的,把老硬盘换下 ...

  5. HAWQ取代传统数仓实践(七)——维度表技术之维度子集

    有些需求不需要最细节的数据.例如更想要某个月的销售汇总,而不是某天的数据.再比如相对于全部的销售数据,可能对某些特定状态的数据更感兴趣等.此时事实数据需要关联到特定的维度,这些特定维度包含在从细节维度 ...

  6. JS返回上一页并刷新

    window.history.go(-1);//返回上一页不刷新 window.location.href = document.referrer;//返回上一页并刷新

  7. (三十六)类数组对象arguments

    类数组对象:arguments 在函数调用时,我们总能见到arguments这个对象,它具体是用来干什么的呢?感觉逼格非常高呢 函数在使用时,我们总会位函数传入各种参数,arguments会将参数储存 ...

  8. Codeforces 589F Gourmet and Banquet

    A gourmet came into the banquet hall, where the cooks suggested n dishes for guests. The gourmet kno ...

  9. 利用Github免费搭建个人主页(个人博客)

    之前闲着, 利用Github搭了个免费的个人主页. 涉及: Github注册 Github搭建博客 域名选购 绑定域名 更多 一  Github注册 在地址栏输入地址:http://github.co ...

  10. codechef Graph on a Table

    codechef Graph on a Table https://www.codechef.com/problems/TBGRAPH 题意 : 一个\(n\times m\)的网格图.\(q\) 个 ...