建表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)變成+ 1, 4、B 線程提交事務 對比版本號,發現說持有的數據和數據中的版本不一致。本次事務回滾. 悲觀鎖實現: 使用with_for_update加鎖,commit後釋放鎖 addr = Address.query.filter_by(user_id=3).with_for_update().first()