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)确保了线程安全。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容