Python學習總結筆記(10)-- MySQL資料庫操作之SQLAlchemy使用總結
SQLAlchemy是一個著名的ORM框架,使用ORM操作資料庫,不用去關注SQL語句本身,這樣可以提高開發的效率。同時使用ORM框架雖然可以減少程式碼編寫的消耗,但是可能也會執行很多冗餘的資料庫操作,降低程式的執行效率。不過總的來說,合理利用ORM框架與資料庫互動還是一個比較不錯的選擇。
0x01 安裝SQLAlchemy
在Python中,有很多ORM框架,SQLAlchemy是其中非常著名的一個框架。可以通過pip來完成安裝:
pip install sqlalchemy
SQLAlchemy提供對很多資料庫的支援。下面我們就以MySQL為例,學習下SQLAlchemy的基本用法。
0x02 連線資料庫
#!/usr/bin/env python
#coding:utf-8
__author__ = 'kikay'
#匯入相關庫
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
#MySQL資料庫連線字串
CONSTR='mysql+mysqlconnector://root:[email protected]:3306/test?charset=utf8'
#初始化資料庫連線物件
engine=create_engine(CONSTR,echo=True)
db_session=sessionmaker(bind=engine)
session=db_session()
上面CONSTR定義了資料庫的連線資訊,其中mysql+mysqlconnector指明瞭呼叫的是mysql-connector模組完成資料庫連線,需要注意charset指定了連線的字符集,可以預設,但是這個引數很重要,設定不當可能導致查詢結果出現亂碼。
sessionmaker()生成資料庫會話類。我們可以把session當成的一個數據庫連線物件。SQLAlchemy維護了一個數據庫連線池(預設為5個連線物件),因此初始化一個會話開銷不會太大。
完成了session物件例項化後,就可以執行SQL語句了。
0x03 execute方式
#獲取當前MySQL中的全部資料庫
dbs=session.execute('show databases;').fetchall()
for db in dbs:
print db
#切換當前資料庫
session.execute('use test;')
row1=session.execute('select * from user where Id>1;').first()
#利用佔位符的方式
row2= session.execute('select * from user where Id>:id',{'id':1}).first()
上面的row1和row2實現的效果是等價的,其中row2使用的是佔位符的方式。其實這種方式就是直接執行SQL語句,和前面講的mysql-connector模板方式類似,不符合ORM思想,這裡就不繼續探討了。
0x04 ORM方式插入
ORM方式很重要的一點就是實現了資料表與類例項的對應關係。這裡我們首先引入一個模組:
from sqlalchemy.ext.declarative import declarative_base
定義實體類的基類:
Base=declarative_base()
然後例項化我們資料庫中的user資料表,完整程式碼如下:
#!/usr/bin/env python
#coding:utf-8
__author__ = 'kikay'
#匯入相關庫
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column,String,Integer,PrimaryKeyConstraint
#MySQL資料庫連線字串
CONSTR='mysql+mysqlconnector://root:[email protected]:3306/test?charset=utf8'
#定義基類
Base=declarative_base()
class User(Base):
#顯示宣告關聯的資料表名稱
__tablename__='user'
#表的結構
#主鍵Id
id=Column(Integer,name='Id',primary_key=True)
#name:非空
name=Column(String(20),nullable=False)
age=Column(Integer,nullable=False)
新增資料:
#初始化資料庫連線物件
engine=create_engine(CONSTR,echo=True)
db_session=sessionmaker(bind=engine)
session=db_session()
user=User(name='kikay',age=20)
#新增
session.add(user)
#提交
session.commit()
#關閉
session.close()
上面演示了一個完整的新增記錄過程。對於SQLAlchemy而言,使用者並沒有與SQL語句直接打交道。
SQLAlchemy新增批量記錄:
#批量新增
for i in xrange(100):
user=User(name='Tom',age=20+i)
#新增
session.add(user)
#提交
session.commit()
#關閉
session.close()
0x05 ORM方式查詢
下面完整定義兩個資料表:
資料表user:
序號 | 欄位名稱 | 資料型別 |
---|---|---|
1 | Id | Int 自增主鍵 |
2 | name | nvarchar(20) 非空 |
3 | age | Int 非空 |
資料表job:
序號 | 欄位名稱 | 資料型別 |
---|---|---|
1 | Id | Int 自增主鍵 |
2 | jobname | nvarchar(200) |
3 | userid | 外來鍵(user.id) |
對應的實體類如下:
#!/usr/bin/env python
#coding:utf-8
__author__ = 'kikay'
#匯入相關庫
from sqlalchemy.orm import sessionmaker,relationship
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column,String,Integer
from sqlalchemy import text,or_,not_,update
from sqlalchemy.dialects.mysql import INTEGER
from sqlalchemy import ForeignKey
#MySQL資料庫連線字串
CONSTR='mysql+mysqlconnector://root:[email protected]:3306/test?charset=utf8'
#定義基類
Base=declarative_base()
class User(Base):
#顯示宣告關聯的資料表名稱
__tablename__='user'
#修改資料表配置
__table_args__={
'mysql_engine':'InnoDB',
'mysql_charset':'utf8'
}
#主鍵Id
id=Column(INTEGER(unsigned=True),name='Id',primary_key=True)
#name:非空
name=Column(String(20),nullable=False)
#age:非空
age=Column(Integer,nullable=False)
#外來鍵物件
#jobs=relationship('job')
class Job(Base):
__tablename__='job'
id=Column(INTEGER(unsigned=True),name='Id',primary_key=True)
jobname=Column(String(200))
#宣告外來鍵
userid=Column(INTEGER,ForeignKey('user.Id',
ndelete='CASCADE',onupdate='CASCADE'))
需要強調的是,在宣告外來鍵時,加上了ondelete=’CASCADE’,onupdate=’CASCADE’,這是因為刪除 user 表的資料,可能會導致 job的外來鍵不指向一個真實存在的記錄,在MySQL資料庫中預設禁止這樣的操作,在InnoDB模式下, 允許指定 ON DELETE 為 CASCADE 和 SET NULL,前者會刪除 job 中無效的記錄,後者會將這些記錄的外來鍵設為 NULL。
除了刪除,還有可能更改主鍵,這也會導致 job 的外來鍵失效。於是相應的就有 ON UPDATE 了。其中 CASCADE 變成了更新相應的外來鍵,而不是刪除。
下面講下基本的查詢、更新、刪除的使用方法:
#查詢物件
query1=session.query(User)
query2=session.query(Job)
查詢全部物件(select * …):
for u in query1.all():
print 'Id:\t',u.id,'\tname:\t',u.name,'\tage:\t',u.age
結果:
Id: 80 name: Tom age: 30
Id: 81 name: Tom3 age: 29
Id: 82 name: Andy age: 25
Id: 88 name: 小花 age: 19
取第1條記錄:
u=query1.first()
print 'Id:\t',u.id,'\tname:\t',u.name,'\tage:\t',u.age
結果:
Id: 80 name: Tom age: 30
條件過濾:
for u in query1.filter(User.id>=82).all():
print 'Id:\t',u.id,'\tname:\t',u.name,'\tage:\t',u.age
等價於(sql語句中的where部分):
for u in query1.filter('id>=82').all():
print 'Id:\t',u.id,'\tname:\t',u.name,'\tage:\t',u.age
根據主鍵值獲取對應的記錄:
u=query1.get(88)
print 'Id:\t',u.id,'\tname:\t',u.name,'\tage:\t',u.age
結果:
Id: 88 name: 小花 age: 19
設定排序:
for u in query1.order_by(User.name.desc()).all():
print 'Id:\t',u.id,'\tname:\t',u.name,'\tage:\t',u.age
結果:
Id: 88 name: 小花 age: 19
Id: 81 name: Tom3 age: 29
Id: 80 name: Tom age: 30
Id: 82 name: Andy age: 25
限制返回的條數:
for u in query1.limit(2).all():
print 'Id:\t',u.id,'\tname:\t',u.name,'\tage:\t',u.age
結果:
Id: 80 name: Tom age: 30
Id: 81 name: Tom3 age: 29
設定查詢的偏移量:
for u in query1.offset(3).all():
print 'Id:\t',u.id,'\tname:\t',u.name,'\tage:\t',u.age
結果:
Id: 88 name: 小花 age: 19
結合偏移量、限制條數以及排序功能,可以方便的實現分頁查詢:
#每頁2條記錄,列印第2頁的內容
for u in query1.order_by(User.id).offset(2).limit(2):
print 'Id:\t',u.id,'\tname:\t',u.name,'\tage:\t',u.age
結果:
Id: 82 name: Andy age: 25
Id: 88 name: 小花 age: 19
返回第一行的第一個欄位:
print session.query(User.name,User.age).filter(User.id==88).scalar()
條件查詢and、or、in、not in、not
#and
print 'And:'
for u in query1.filter(User.id>80,User.age>25).all():
print 'Id:\t',u.id,'\tname:\t',u.name,'\tage:\t',u.age
#or
print 'Or:'
for u in query1.filter(or_(User.id>88,User.age>29)).all():
print 'Id:\t',u.id,'\tname:\t',u.name,'\tage:\t',u.age
#in
print 'in:'
for u in query1.filter(User.id.in_((79,81,90))).all():
print 'Id:\t',u.id,'\tname:\t',u.name,'\tage:\t',u.age
#not in
print 'not in:'
for u in query1.filter(User.id.notin_((81,82,88))).all():
print 'Id:\t',u.id,'\tname:\t',u.name,'\tage:\t',u.age
#not
print 'not:'
for u in query1.filter(not_(or_(User.name=='Tom',User.name==u'小花'))).all():
print 'Id:\t',u.id,'\tname:\t',u.name,'\tage:\t',u.age
結果:
And:
Id: 81 name: Tom3 age: 29
Or:
Id: 80 name: Tom age: 30
in:
Id: 81 name: Tom3 age: 29
not in:
Id: 80 name: Tom age: 30
not:
Id: 81 name: Tom3 age: 29
Id: 82 name: Andy age: 25
返回記錄的總條數:
print query1.filter(User.id>=81).count()
模糊查詢:
for u in query1.filter(User.name.like('%t%')).all():
print 'Id:\t',u.id,'\tname:\t',u.name,'\tage:\t',u.age
結果:
Id: 80 name: Tom age: 30
Id: 81 name: Tom3 age: 29
0x06 ORM方式更新與刪除
更新:
print 'old name:',query1.filter(User.id==80).one().name
query1.filter(User.id==80).update({User.name:'Tom2'})
session.commit()
print 'new name:',query1.filter(User.id==80).one().name
結果:
old name: Tom
new name: Tom2
刪除:
query1.filter(User.id<=80).delete()
需要注意的是,update和delete在做批量操作的時候(使用 where…in(…))操作,需要指定synchronize_session的值。
query1.filter(User.id.in_((1,2,3))).update({User.name:'Test'})
query1.filter(User.id.in_((1,2,3))).delete()
報錯:
sqlalchemy.exc.InvalidRequestError: Could not evaluate current criteria in Python. Specify 'fetch' or False for the synchronize_session parameter.
修改如下:
query1.filter(User.id.in_((1,2,3))).update({User.name:'Test'},synchronize_session=False)
query1.filter(User.id.in_((1,2,3))).delete(synchronize_session=False)
0x07 後記
上面簡單介紹了下SQLAlchemy的使用方法,主要還是關注在增刪改查方面,其實還有很多知識點沒有講到,有興趣的可以自己接著研究下。ORM框架使用起來很方便,但是要想正確理解ORM,前提還是要對關係型資料庫的基本原理有所瞭解。