1. 程式人生 > >Python學習總結筆記(10)-- MySQL資料庫操作之SQLAlchemy使用總結

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,前提還是要對關係型資料庫的基本原理有所瞭解。