Flask-SQLAlchemy数据库操做

建表sql

 1 # -*- coding: UTF-8 -*-
 2 
 3 from . import db  4 
 5  
 6 
 7 #多对多关系表,两组一对多,即为多对多
 8 
 9 class Boy2Girl(db.Model):  10 
 11     __tablename__ = 'boy2girl'
 12 
 13     nid = db.Column(db.Integer,primary_key=True)  14 
 15     #创建一对多关系,ForeignKey传入对应表的__tablename__.id
 16 
 17     boy_id = db.Column(db.Integer,db.ForeignKey('boy.id'))  18 
 19     # 创建一对多关系,ForeignKey传入对应表的__tablename__.id
 20 
 21     girl_id = db.Column(db.Integer,db.ForeignKey('girl.id'))  22 
 23  
 24 
 25 class Boy(db.Model):  26 
 27     __tablename__ = 'boy'
 28 
 29     id = db.Column(db.Integer,primary_key=True)  30 
 31     name = db.Column(db.String(32),unique=True)  32 
 33     age = db.Column(db.Integer)  34 
 35     #并不会在数据库中生成列,做用为ORM调用时能够经过.来调用另外一张表
 36 
 37     #例如boy.girl,girl.boy
 38 
 39     #传入的参数为另外一张表的类名,关系表的表名,反向查询时的名字,能够为任意名字
 40 
 41     girl = db.relationship('Girl',secondary = Boy2Girl.__tablename__,backref = 'boy')  42 
 43     car = db.relationship('Car',backref = 'boy')  44     
 45     #自引用
 46     
 47     father_id = db.Column(db.Integer,db.ForeignKey('boy.id'))  48     
 49     #自引用时需添加remote_side=[id],一对一添加uselist=Flase
 50     
 51     father = db.relationship('Comment',backref = 'child',remote_side=[id],uselist=Flase)  52     
 53     def __repr__(self):  54 
 55         return '<Boy %r>'%self.name  56 
 57 
 58 #高级多对多,关联表和普通多对多一致,好处是能够直接访问关联表,调用其中的其余字段
 59         
 60 #多对多关系表,两组一对多,即为多对多
 61 
 62 class Girl2Car(db.Model):  63 
 64     __tablename__ = 'girl2car'
 65 
 66     nid = db.Column(db.Integer,primary_key=True)  67 
 68     #创建一对多关系,ForeignKey传入对应表的__tablename__.id
 69 
 70     car_id = db.Column(db.Integer,db.ForeignKey('car.id'))  71 
 72     # 创建一对多关系,ForeignKey传入对应表的__tablename__.id
 73 
 74     girl_id = db.Column(db.Integer,db.ForeignKey('girl.id'))  75     
 76     create_time = db.Column(db.DateTime,default=datetime.datetime.utcnow)  77         
 78 
 79 class Girl(db.Model):  80 
 81     __tablename__ = 'girl'
 82 
 83     id = db.Column(db.Integer,primary_key=True)  84 
 85     name = db.Column(db.String(32),unique=True)  86 
 87     age = db.Column(db.Integer)  88     
 89     #cascade删除时删除全部关联项
 90     
 91     #高级多对多,须要指定foreign_keys = [Girl2Car.car_id],手工管理外键
 92     
 93     car = db.relationship('Girl2Car',foreign_keys = [Girl2Car.car_id],backref=db.backref('girl',lazy='joined'),  94                                lazy='dynamic',cascade='all,delete-orphan')  95                                
 96 
 97     
 98     def __repr__(self):  99 
100         return '<Girl %r>' % self.name 101 
102  
103 
104 class Car(db.Model): 105 
106     __tablename__ = 'car'
107 
108     id = db.Column(db.Integer,primary_key=True) 109 
110     name = db.Column(db.String(32),unique=True) 111 
112     #创建一对多关系,ForeignKey传入对应表的__tablename__.id,写在多的一对多中多的那边
113 
114     boy_id = db.Column(db.Integer,db.ForeignKey('boy.id')) 115     
116     #cascade删除时删除全部关联项
117     
118     #高级多对多,须要指定foreign_keys = [Girl2Car.girl_id],手工管理外键
119 
120     girl = db.relationship('Girl2Car',foreign_keys = [Girl2Car,girl_id],backref=db.backref('car',lazy='joined'), 121                                lazy='dynamic',cascade='all,delete-orphan') 122 
123     def __repr__(self): 124 
125         return '<Car %r>'%self.name 126         
127

 

查询shell

 1 #查询所有
 2 boys = Boy.query.all()  3 
 4 #排序
 5 boys = Boy.query.order_by(Boy.age.desc()).all()  6 
 7 #`filter_by`和`filter`都是过滤条件,只是用法有区别`filter_by`里面能用`=`,不能用`!= `还有`> <` 等等
 8 
 9 #过滤条件查询一个
10 boy1 = Boy.query.filter_by(id=1).first() 11 boy1 = Boy.query.filter(Boy.id=1).first() 12 
13 #过滤条件查多个
14 boys = Boy.query.filter(Boy.id>1).all() 15 
16 #切片查询
17 #限制返回条数
18 boys = Boy.query.filter(Boy.name!='jingqi').limit(2).all() 19 #从第三条开始返回查询结果
20 boys = Boy.query.filter(Boy.name!='jingqi').offset(2).all() 21 #切片返回记录
22 boys = Boy.query.filter(Boy.name!='jingqi').slice(2,3).all() 23 
24 #模糊匹配,是十分耗费时间的,能不用就尽可能不要用。
25 boys = Boy.query.filter(Boy.name.like('jingqi')).all() 26 boys = Boy.query.filter(Boy.name.notlike('jingqi')).all() 27         
28 #成员属于
29 boys = Boy.query.filter(Boy.name.in_(['jingqi','jingqi1'])).all() 30 boys = Boy.query.filter(Boy.name.notin_(['jingqi','jingqi1'])).all() 31         
32 #选择条件
33 from flask_sqlalchemy import or_,and_,all_,any_ 34 boys = Boy.query.filter(or_(User.name=='jingqi',User.age==12)).all() 35 boys = Boy.query.filter(and_(User.name=='jingqi',User.age==12)).all() 36 

 

 分页查询数据库

第一个参数表示当前页,第二个参数表明每页显示的数量,error_out=True的状况下若是指定页没有内容将出现404错误,不然返回空的列表flask

 1 #分页查询,page为第几页
 2 pagination = Boy.query.order_by(Boy.id.desc()).paginate(page, per_page=10, error_out=False)  3 boys = pagination.items  4 
 5 has_next :是否还有下一页  6 has_prev :是否还有上一页  7 items : 返回当前页的全部内容  8 next(error_out=False) : 返回下一页的Pagination对象  9 prev(error_out=False) : 返回上一页的Pagination对象 10 page : 当前页的页码(从1开始) 11 pages : 总页数 12 per_page : 每页显示的数量 13 prev_num : 上一页页码数 14 next_num :下一页页码数 15 query :返回 建立这个Pagination对象的查询对象 16 total :查询返回的记录总数

 

增删改session

#建立删除表,能够在flask shell中操做
db.create_all() db.drop_all() # user1 = User(username='Jim',role=admin_role) db.session.add(user1) db.session.commit() #
db.session.delete(admin_role) db.session.commit() #
admin_role.name = 'AD' db.session.add(admin_role) db.session.commit() #回滚,在commit前均可以回滚到上次commit时的状态
db.seesion.rollback()

 

原生sql语句并发

1 id = request.args.get('id') 2 #原生sql
3 user1 = db.session.execute("SELECT * from user WHERE id="+id) 4 #防注入,使用占位符
5 conn = db.session.connection() 6 user1 = conn.execute("SELECT * from user WHERE id=%s", [id])

 

高级部分,如下部分搞清楚后才能更好的作优化app

高级查询能够尝试先写原生sql,再转换为ormide

 1 #须要原生sqlalchemy中调用func,能够经过func.的方式使用sql中的内置函数
 2 from sqlalchemy import func  3 
 4 #联合查询,join,尽可能不使用联合查询,联合查询优于子查询
 5 all_school_count = School.query.join(User).filter(School.leader_id == user1.id).count()  6 
 7 #分组查询,使用having限制条件,group_by,having
 8 all_school_count = db.session.query(func.count(School.id)).group_by(School.leader_id).having(func.count(School.id)>1).first()[0]  9 
10 #偏移offset,限制数量limit,
11 user1_schools = School.query.filter(School.leader_id==user1.id).offset(5).limit(3).all() 12 
13 #slice, sliece(start, end) 从start取到end.
14 user1_schools = School.query.filter(School.leader_id == user1.id).slice(5, 8).all() 15 
16 
17 
18 #事务,正常时手动commit,异常时手动回滚rollback,所有成功或者所有回滚
19 try: 20    school1 = School(name='transaction'+str(num), leader_id=1) 21  db.session.add(school1) 22    school2= School(name='transaction'+str(int(num)+1), leader_id=1) 23  db.session.add(school2) 24 except: 25  db.session.rollback() 26 else: 27  db.session.commit() 28 
29 #年级
30 class Grade(db.Model): 31     __tablename__ = 'grade'
32     id = db.Column(db.Integer, primary_key=True, autoincrement=True) 33     name = db.Column(db.String(64), nullable=False,index=True) 34 
35     school_id = db.Column(db.Integer, db.ForeignKey('school.id',ondelete='RESTRICT')) 36     school = db.relationship('School', foreign_keys=school_id, backref='grade') 37 
38     from sqlalchemy import text, event 39     #触发器,定义在模型中
40  @staticmethod 41     def chang_all_grade_count(school_id): 42         #子查询
43         user1 = User.query.filter(User.id==(db.session.query(School.leader_id).filter(School.id == school_id)).subquery()).first() 44         all_grade_count = user1.all_grade_count 45         all_grade_count+=1
46  db.session.execute(text( 47             "update `user` set all_grade_count=:all_grade_count where id=:user_id"), 48             {'user_id': user1.id,'all_grade_count':all_grade_count}) 49 
50  @staticmethod 51     def on_insert(mapper, connection, target): 52  Grade.chang_all_grade_count(target.school_id) 53 
54  @staticmethod 55     def on_delete(mapper, connection, target): 56  Grade.chang_all_grade_count(target.school_id) 57 
58     def __repr__(self): 59         return '<Grade %r>' % self.name 60 
61 #注册触发器,在增删改时触发
62 db.event.listen(Grade,'after_insert',Grade.on_insert) 63 db.event.listen(Grade,'after_delete',Grade.on_delete)

经过上下文管理器的方式管理事务函数

''' 集成并在本来的SQLAlchemy类中添加新功能,方便使用 '''
from flask_sqlalchemy import SQLAlchemy as _SQLAlchemy class SQLAlchemy(_SQLAlchemy): #事务上下文管理器
 @contextmanager def auto_commit(self): try: yield self.session.commit() except Exception as e: self.session.rollback() raise e db = SQLAlchemy()

 

lazy和ondelete性能

relationship中的lazy属性 select: 首次访问按需加载; immediate: 源对象加载后就加载; joined: 加载记录,但使用链接; subquery: 当即加载记录,使用子查询; noload: 永不加载; dynamic: 不加载记录,但提供加载记录的查询。 #直接获取backref的数据,查询结果为数据列表
lazy="select"
#查询结果为查询对象,只能够用在一对多和多对多关系中,不能够用在一对一和多对一中
lazy="dynamic"
#表面看查询结果同dynamic,但sql层面是经过join完成的,因此是一次查询,结果都出来了 #但也查询出了不少冗余数据,且join查询自己较慢,而dynamic须要屡次查询
lazy="joined" 使用ondelete指定约束, 外建约束有如下几种: RESTRICT:删除父表数据时,若是子表有数据在使用该字段的数据时,会阻止删除(默认为此约束) NO ACTION:在MySQL中,同RESTRICT CASCADE:级联删除,删除父表的某一条数据时,子表中使用该外建的数据也会被删除 SET NULL:父表数据被删除,删除父表的某一条数据时,子表中使用该外建的数据设置为NULL #删除父数据,与其关联的子数据所有删除
relationship中设置cascade='all, delete-orphan' 与 passive_deletes = True, ForeignKey中设置ondelete = 'CASCADE'

 

乐观锁、悲观锁

乐观锁,假定操做不会修改数据,在查询时正常查询,在修改时作判断 适合查多改少,常常被并发修改的数据可能总是出现版本不一致致使有的线程操做经常失败 悲观锁,假定操做会修改数据,在查询时加锁,修改完后释放锁 适合短事务(长事务致使其它事务一直被阻塞,影响系统性能),查少该多 乐观锁实现: 为数据表新加一列version 一、A 线程准备往小明的帐户上加100, 1, 读取到小明 有 1000 元, 1000 + 100 事务未提交 ,读取到的版本号(oversion)为0 二、B线程准备往小明的帐户上加100, 1, 读取到小明 有 1000 元, 1000 + 100 事务未提交 ,读取到的版本号(oversion)为0 三、A 线程提交事务,对比版本号,若是数据库该条数据的版本号和线程所持有该条数据的版本号一致,说明数据没有修改过。
更新余额以及版本号 小明帐户余额变成1100 版本号(version)变成+ 14、B 线程提交事务 对比版本号,发现说持有的数据和数据中的版本不一致。本次事务回滚. 悲观锁实现: 使用with_for_update加锁,commit后释放锁 addr = Address.query.filter_by(user_id=3).with_for_update().first()
相关文章
相关标签/搜索