zoukankan      html  css  js  c++  java
  • Flask 数据库多对多关系

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

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

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

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

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

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

    [python] view plain copy
     
    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.   
    6.   
    7. class Student(db.Model):  
    8.     id = db.Column(db.Integer, primary_key=True)  
    9.    name = db.Column(db.String)  
    10.    classes = db.relationship('Class',secondary=registrations,  
    11.                                     backref=db.backref('students', lazy='dynamic'),  
    12.                                     lazy='dynamic')  
    13.   
    14.   
    15. class Class(db.Model):  
    16.     id = db.Column(db.Integer, primary_key = True)  
    17.     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.定义模型

    [python] view plain copy
     
    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.     .............省略其他字段  
    7.     departments=db.relationship('Department', secondary=user_department, backref=db.backref('users',lazy='dynamic'), lazy='dynamic')  
    8.       
    [python] view plain copy
     
    1. class Department(db.Model):  
    2.     __tablename__ = 'departments'  
    3.     id = db.Column(db.Integer, primary_key=True)  
    4.     department = db.Column(db.String(100))  
    [python] view plain copy
     
    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.定义表单:

    [python] view plain copy
     
    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.       
    8.     submit = SubmitField('Submit')  
    9.   
    10.    #下拉菜单初始化  
    11.     def __init__(self, user, *args, **kwargs):  
    12.         super(SmForm, self).__init__(*args, **kwargs)  
    13.         <strong>self.pre_department.choices = [(pre_department.id, pre_department.department)  
    14.                              for pre_department in Department.query.order_by(Department.department).all()]  
    15.         self.cut_department.choices = [(cut_department.id, cut_department.department)  
    16.                              for cut_department in Department.query.order_by(Department.department).all()]</strong>  
    17.          
    18.         self.user = user  

    3.定义路由:

    [python] view plain copy
     
    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.   
    10.             # User的学院更新  删除旧的数据,<strong>联合删除  
    11.             usr = current_user._get_current_object()  
    12.             deparment = user.departments.all()  
    13.             for de in deparment:  
    14.                 de.users.remove(usr)</strong>  
    15.   
    16.        ........................省略其他  
    17.   
    18.               
    19.             user.is_departmentChange = form.is_departmentChange.data  
    20.           <strong>#向关系表中添加  
    21.             user.departments.append(Department.query.get(form.pre_department.data))  
    22.             user.departments.append(Department.query.get(form.cut_department.data))  
    23.   
    24.             db.session.add(user)  
    25.             db.session.commit()</strong>  
    26.             return redirect(url_for('.sm_success'))  
    27.     return render_template('sm.html', form=form)  

    4.渲染模板(省略)

  • 相关阅读:
    Linux 守护进程创建
    Linux 进程
    静态库与动态库的制作
    目录文件的操作函数 mkdir ,opendir,readdir,closedir
    获取文件或目录的属性 stat 函数
    文件IO 例子
    文件 IO
    标准 IO fread 与 fwrite 的使用(可以实现二进制流的读写)
    bzoj 2716: [Violet 3]天使玩偶
    cf1175 DE
  • 原文地址:https://www.cnblogs.com/kiko0o0/p/8444308.html
Copyright © 2011-2022 走看看