2018-12-20 模型关系和钩子函数

一、模型关系定义

1.1 一对多

class Student(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(20), unique=True, nullable=True)
    grade_id = db.Column(db.Integer, db.ForeignKey('tb_grade.id'), nullable=True)

    __tablename__ = "tb_student"


class Grade(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    g_name = db.Column(db.String(10), unique=True, nullable=False)
    stus = db.relationship('Student', backref='g')
    # backref 反向引用

    __tablename__ = 'tb_grade'

    db.Column(db.Integer, db.ForeignKey('tb_grade.id'), nullable=True) 创建外键关联关系,外键一般在多的一方
    db.relationship('Student', backref='g')  通过relationship('模型名',backref='字符') 
    relationship() 中第一个参数是模型名,
    brakref 反向引用(在多查一时使用该参数)

  一对多关系中的查询操作

    # 查询班级
    grade = Grade.query.filter(Grade.g_name=='python001').first()
    # 通过班级查询学生
    students = grade.stus


    stus = Student.query.get(1)
    # 通过学生查询班级
    grade = stus.g

1.2 一对一

    一对一关系也就是在一对多关系中将relationship中添加
    uselist = True 
    也可以在外键中添加唯一约束
    unique = True

1.3 多对多

  创建多对多关系

class Student(db.Model):
    # 创建学生模型
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(20), unique=True, nullable=True)
    grade_id = db.Column(db.Integer, db.ForeignKey('tb_grade.id'), nullable=True)

    __tablename__ = "tb_student"

# 创建第三张表
s_c = db.Table('s_c',
               db.Column('s_id', db.Integer, db.ForeignKey('tb_student.id')),
               db.Column('c_id', db.Integer, db.ForeignKey('tb_course.id')),
               )


class Course(db.Model):
    # 创建课程模型
    __tablename__ = 'tb_course'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    g_name = db.Column(db.String(10), unique=True, nullable=False)
    stus = db.relationship('Student', secondary=s_c, backref='cou')

    在多对多关系中relationship可以定义在任何一方,但是这里的relationship参数和一对多关系中的参数有些不同
    格式:
        db.relationship('模型名', secondary=第三张表明, backref='反向引用字符(任意字符都行)')
    如:
        stus = db.relationship('Student', secondary=s_c, backref='cou')

  多对多关系中的查询操作

    在多对多关系中相互查询操作与一对多一样
    stu = Student.query.get(1)
    cou = Course.query.filter(Course.g_name=='java').first()
    
    # 学生查询课程, 返回一个由课程对象组成的列表
    stu.cou
    # 课程查询学生,返回一个由学生对象组成的列表
    cou.stus

  向第三张表中添加和删除数据

    在flask中维持多对多关系时,创建了第三张表但第三张表并不是一个模型,所以不能直接操作该表,这也是flask与Django在定义模型上的一点区别。
    
    不管是通过学生查询课程还是课程查询学生,两个方法返回值都是一个列表,所以要向第三张表中插入数据就可以直接通过列表操作,实现对第三张表的操作。
# 获取id为1, 2的学生对象
stu1 = Student.query.get(1)
stu2 = Student.query.get(2)
# 获取课程名为java的课程
cous = Course.query.filter(Course.g_name=='java').first()

# 给学生添加课程
stu1.cou.append(cou)

# 给课程添加一个选课学生
cous.stus.append(stu2)

# 删除第三张表的数据也可以通过列表操作实现,列表删除元素remove(元素)
# 删除一个选课学生
cous.stus.remove(stu1)

# 删除一门学生已选课程
stu2.cou.remove(cous)

# 最后提交事务
db.session.commit()

在模型定义是relationship有一个lazy参数,
官网解释有如下几个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

一般常用的值如下:

lazy=select/True 就是访问到属性的时候,就会全部加载该属性的数据。
lazy=joined/False 则是在对关联的两个表进行join操作,从而获取到所有相关的对象。
lazy=dynamic 则不一样,在访问属性的时候,并没有在内存中加载数据,而是返回一个query对象,如:filter过滤、all()等

二、钩子函数()

在flask中没有中间件概念,但是在蓝图对象(Blueprint)中提供了常用的几个装饰器类似于Django中的中间件。被这些装饰器修饰的函数就称为钩子函数。

2.1 before_request

见名知意,该装饰器装饰的函数会在请求之前调用,也就类似于Django中间件中的process_request,会对所有请求进行拦截。
注意: 在before_request中不能写return语句
因为当你的请求在before_request中就遇到return服务器就会立即响应你的请求,并没有调用相应的视图函数。

blue = Blueprint('app', __name__)

@blue.before_request
def before_req():

    print('请求之前执行的代码')

如果有多个被before_request装饰的函数,则会依次调用

2.2 after_request

after_request装饰的函数会在请求结束后调用,类似于Django中的process_response,同时after_request装饰的函数需要接收一个响应response,并且必须写return

@blue.after_request
def after_req(response):

    print('请求之后执行的代码')

    return response

有多个被after_response装饰的函数会在请求后,倒序依次执行,因为请求和响应的执行过程成 U 型,先执行的请求对应的响应最后执行,后执行的请求对应的响应先执行。

  中间件的执行简单过程

中间件执行过程

2.3 teardown_request

被该装饰器装饰的函数无论程序是否正常执行都会被调用,类似于try...except...finally 中的finally
但是,被装饰函数需要接收一个异常
我们可以将一些无论程序成功与否都要执行的操作写在这里面,如:连接数据库,我们要关闭连接

@blue.teardown_request
def teardown_req(e):

    print(e)

  注意:想要teardown_request生效需要加下面的配置

    app.config['PRESERVE_CONTEXT_ON_EXCEPTION'] = False

2.4 抛出异常

在flask中可以用abort(异常值),抛出一个异常
from flask import abort

try:
    1/0
except:
    abort(500)

2.5 异常捕获

异常捕获函数可以用errorhandler(异常类型)


@blue.errorhandler(500)
def error(e):

    print(e)

    return e

2.6 下面是一个连接数据库的实例


@blue.before_request
def connect_mysql():
    # 创建连接
    conn = pymysql.Connect(host='127.0.0.1',
                           port=3306,
                           database='flaskdb7',
                           user='root',
                           password='123456')
    # 获取游标
    cursor = conn.cursor
    # 绑定全局变量,由于flask提供了一个g对象用于绑定全局变量
    # from flask import g
    g.cursor = cursor
    g.conn = conn


@blue.route('/sel_stu/')
def sel_stu():
    sql = 'select * from tb_student'

    # 执行sql语句
    g.cursor.execute(sql)
    # 获取查询结果
    students = g.cursor.fetchall()
    print(students)

    return '查询成功'

# 这里我们就用上面提到的teardwn_request来做关闭连接
# 无论程序运行情况怎么样我们都要关闭此次连接
@blue.teardown_request
def close_mysql(e):

    g.cursor.close()

    return e

  注意:应用上下文G对象

应用全局对象(g)是Flask为每一个请求自动建立的一个对象。g的作用范围只是在一个请求(也就是一个线程)里,它不能在多个请求中共享数据,故此应用全局变量(g)确保了线程安全。

推荐阅读更多精彩内容