<time datetime="2019-04-19T12:31:08.000Z" data-reader-unique-id="339" class="date" style="margin: 0px; max-width: 100%; font-size: 1em !important; font-weight: normal !important; font-style: normal !important; display: inline !important;">2019.04.19 20:31:08</time>
SQLAlchemy是python中最著名的ORM(Object Relationship Mapping)框架了刽肠。
前言:什么是ORM咖楣?
ORM操作是所有完整軟件中后端處理最重要的一部分罗售,主要完成了后端程序和數(shù)據(jù)庫之間的數(shù)據(jù)同步和持久化的操作震庭。
數(shù)據(jù)庫表示一個二維表征炼,包含多行多列间聊。把一個表的內(nèi)容用python的數(shù)據(jù)結(jié)構(gòu)表示出來的話笤虫,可以用一個list表示多行,list的每一個元素是tuple直撤,表示一行記錄非竿,比如,包含id和name的user表:
[圖片上傳失敗...(image-82751b-1586172584834)]
Python的DB-API返回的數(shù)據(jù)結(jié)構(gòu)就是像上面這樣表示的谋竖。但是用tuple表示一行很難看出表的結(jié)構(gòu)汽馋,如果把一個tuple用class實(shí)例來表示,就可以更容易的看出表的結(jié)構(gòu)來:
[圖片上傳失敗...(image-3b0876-1586172584834)]
這就是傳說中的ORM技術(shù):Object-Relational Mapping 圈盔,把關(guān)系數(shù)據(jù)庫的表結(jié)構(gòu)映射到對象上豹芯。但是由誰來做這個轉(zhuǎn)化呢?所以O(shè)RM框架應(yīng)運(yùn)而生驱敲。目前來說也是描述程序中對象和數(shù)據(jù)庫中數(shù)據(jù)記錄之間的映射關(guān)系的統(tǒng)稱铁蹈,是一種進(jìn)行程序和數(shù)據(jù)庫之間數(shù)據(jù)持久化的一種編程思想。
ORM思想的核心是隱藏了數(shù)據(jù)訪問細(xì)節(jié)众眨,提供了通用的數(shù)據(jù)庫交互握牧,并且完全不用考慮SQL語句容诬,從而快速開發(fā)。
一句話解釋ORM就是:一種可以把model中的模型和數(shù)據(jù)庫中的一條數(shù)據(jù)相互轉(zhuǎn)換的工具沿腰。
舉個例子:
[圖片上傳失敗...(image-3804b0-1586172584834)]
[圖片上傳失敗...(image-5837f6-1586172584834)]
在Python中览徒,最有名的ORM框架是SQLAlchemy。我們來看看其語法颂龙。
一习蓬,初始化數(shù)據(jù)庫連接
SQLAlchemy本身無法操作數(shù)據(jù)庫,其必須通過pymysql等第三方插件措嵌。上圖中Dialect用于和數(shù)據(jù)API進(jìn)行交流躲叼,根據(jù)配置文件的不同調(diào)用不同的數(shù)據(jù)庫API,從而實(shí)現(xiàn)對數(shù)據(jù)庫的操作企巢。
使用sqlalchemy進(jìn)行數(shù)據(jù)庫操作枫慷,首先我們需要建立一個指定數(shù)據(jù)庫的鏈接引擎對象,而建立引擎對象的方式被封裝在了sqlalchemy.create_engine函數(shù)中浪规,通過指定的數(shù)據(jù)庫連接信息即可創(chuàng)建或听。
create_engine()用來初始化數(shù)據(jù)庫連接。SQLAlchemy用一個字符串表示連接信息:
'數(shù)據(jù)庫類型+數(shù)據(jù)庫驅(qū)動名稱://用戶名:口令@機(jī)器地址:端口號/數(shù)據(jù)庫名'
你只需要根據(jù)需要替換掉用戶名笋婿,口令等信息即可誉裆。舉個例子:
初始化數(shù)據(jù)庫鏈接
engine = create_engine('mysql+mysqlconnector://root:123456@localhost:3306/test')
我使用了mysql數(shù)據(jù)庫,數(shù)據(jù)庫連接框架用的是mysqlconnector萌抵,用戶名為root,密碼是123456元镀,端口號是localhost(127.0.0.1)绍填,端口號是3306(mysql服務(wù)器默認(rèn)端口號),test是數(shù)據(jù)庫的名字栖疑。
二讨永,創(chuàng)建user模型及其數(shù)據(jù)庫建表操作
2.1,安裝SQLAlchemy
如果沒有安裝SQLAlchemy請先安裝遇革,這個不是python自帶的卿闹。
pip install sqlalchemy
2.2,數(shù)據(jù)庫建表
在test數(shù)據(jù)庫中萝快,創(chuàng)建user表锻霎,SQL如下:
create table user (id varchar(20) primary key, name varchar(20))
創(chuàng)建表,表名為user揪漩,表中有兩個字段旋恼。一個是id,varchar類型奄容,最多支持20個字符冰更,設(shè)置為主鍵产徊,另一個是name,varchar類型蜀细,最多支持20個字符舟铜。
(PS:主鍵時唯一的,當(dāng)你重復(fù)插入時會報錯奠衔,并終止插入操作)谆刨。
插入數(shù)據(jù),如下:
[圖片上傳失敗...(image-2c6faa-1586172584834)]
更改表名
alter table origin_name rename to new_name;
2.3涣觉,創(chuàng)建user模型
在model.py中創(chuàng)建 一個User類痴荐,用來和User表中的字段進(jìn)行關(guān)聯(lián)。
[圖片上傳失敗...(image-72132b-1586172584834)]
這段代碼的意思官册,User類的字段名和表中完全一致生兆,而且字段的屬性也一樣,以id字段為例膝宁,表中id的屬性是Varchar(20)鸦难,primary key,模型中String(20)员淫,primary_key=True合蔽。我們可以清洗的看到varchar在程序中對應(yīng)String,primary_key對應(yīng)程序中的primary_key屬性介返,而且是一個bool類型拴事。
(PS:這里的String是sqlAlchemy中的一個類,這樣類似的類我們還會用到Column)
2.4圣蝎,Column類的學(xué)習(xí)
構(gòu)建函數(shù)為
Column.init(self, name, type_, *args, **kwargs)
name 列名
type_ 類型 刃宵,更多類型SQLAlchemy.types
下面是*args參數(shù)定義
Constraint(約束)
ForeignKey(外鍵)
ColumnDefault(默認(rèn))
Sequenceobjects(序列)定義
下面是**kwargs參數(shù)定義
primary_key 如果為True,則是主鍵
nullable 是否可謂Null徘公,默認(rèn)是True
default 默認(rèn)值牲证,默認(rèn)是None
index 是否是索引,默認(rèn)是True
unique 是否唯一鍵关面,默認(rèn)是False
onupdate 指定一個更新時候的值坦袍,這個操作是定義在SQLAlchemy中,不是在數(shù)據(jù)庫里的等太,當(dāng)更新一條數(shù)據(jù)時設(shè)置捂齐,大部分用于update Time這類字段
autoincrement 設(shè)置為整型自動增長,只有沒有默認(rèn)值缩抡,并且是Integer類型辛燥,默認(rèn)是True
quote 如果列明是關(guān)鍵字,則強(qiáng)制轉(zhuǎn)義,默認(rèn)False
三挎塌,CRUD(Create Read Update Delete徘六,增查改刪)
在SQLAlchemy中,增刪改查操作是通過一個session對象(DBSession是由sessionmaker創(chuàng)建的)來完成的榴都。
3.1待锈,初始化DBSession,連接會話
我們的程序中的對象要使用sqlalchemy的管理嘴高,實(shí)現(xiàn)對象的ORM操作竿音,就需要按照框架指定的方式進(jìn)行類型的創(chuàng)建操作,SQLAlchemy封裝了基礎(chǔ)類的聲明操作和字段屬性的定義限制方式拴驮,開發(fā)人員要做的事情就是引入需要的模塊并在創(chuàng)建對象的時候使用他們即可春瞬。
基礎(chǔ)類封裝在sqlalchemy.ext.declarative_base模塊中,字段屬性的定義封裝在SQLAlchemy模塊中套啤,通過sqlalchemy.Column定義屬性宽气,通過封裝的Integer,String潜沦,F(xiàn)loat等定義屬性的限制萄涯。
首先我們需要創(chuàng)建DBSession,在此基礎(chǔ)上就可以進(jìn)行增刪改查操作了唆鸡。
[圖片上傳失敗...(image-132536-1586172584834)]
上面的代碼完成SQLAlchemy的初始化和具體每個表的class定義涝影。如果有多個表,就繼續(xù)定義其他class争占。
注意:如果在創(chuàng)建會話的時候還沒有指定數(shù)據(jù)庫引擎燃逻,可以通過如下的方式完成會話:
3.2,添加操作
程序中存在一個對象Object數(shù)據(jù)臂痕,通過ORM核心模塊進(jìn)行增加的函數(shù)定義將對象保存在數(shù)據(jù)庫的操作過程伯襟,就是增加操作。比如注冊操作中刻蟹,通過用戶輸入的賬號密碼等信息創(chuàng)建了一個獨(dú)立的對象逗旁,通過add()函數(shù)將對象增加保存到數(shù)據(jù)庫中嘿辟,數(shù)據(jù)庫中就存在用戶這個對象數(shù)據(jù)了舆瘪。
下面,我們看看如何向數(shù)據(jù)庫表中添加一行記錄红伦。
由于有ORM英古,我們向數(shù)據(jù)庫表中添加一行記錄,可以視為添加一個User對象:
[圖片上傳失敗...(image-919e2c-1586172584834)]
可見昙读,關(guān)鍵是獲取session召调,然后把對象添加到session,最后提交并關(guān)閉。DBSession對象可視為當(dāng)前數(shù)據(jù)庫連接唠叛。
3.3只嚣,查詢操作
查詢時通過Session的query()方法創(chuàng)建一個查詢對象,這個函數(shù)的參數(shù)可以是任何類或者類的描述的集合艺沼。查詢出來的數(shù)據(jù)是一個對象册舞,直接通過對象的屬性調(diào)用。
如何從數(shù)據(jù)庫表中查詢數(shù)據(jù)呢障般?有了ORM调鲸,查詢出來的可以不再是tuple,而是User對象挽荡。SQLAlchemy提供的查詢接口如下:
可見藐石,ORM就是把數(shù)據(jù)庫表的行與相應(yīng)的對象建立關(guān)聯(lián),互相轉(zhuǎn)換定拟。
由于關(guān)系數(shù)據(jù)庫的多個表還可以用外鍵實(shí)現(xiàn)一對多于微,多對多等關(guān)聯(lián),相應(yīng)的办素,ORM框架也可以提供兩個對象之間的一對多角雷,多對多等功能。
例如性穿,如果一個User擁有多個Book勺三,就可以定義一對多關(guān)系如下:
當(dāng)我們查詢一個User對象時,該對象的books屬性將返回一個包含若干個Book對象的list .
3.4需曾,更新操作
程序中存在的一個對象Object數(shù)據(jù)吗坚,有自己的id編號(可以使程序中自行賦值定義,更多的操作是從數(shù)據(jù)庫中查詢出來存在的一個對象)呆万,通過ORM核心模塊進(jìn)行修改函數(shù)的定義將對象改變的數(shù)據(jù)更新到數(shù)據(jù)庫中已經(jīng)存在的記錄中的過程商源,就是更新操作。比如用戶更改登錄密碼操作時谋减,根據(jù)程序中查詢得到的一個用戶{id編號牡彻,賬號,密碼}出爹,在程序中通過改變其密碼屬性數(shù)據(jù)庄吼,然后通過update()函數(shù)將改變的數(shù)據(jù)更新保存到數(shù)據(jù)庫中,數(shù)據(jù)庫原來的數(shù)據(jù)就發(fā)生了新的改變严就。
更新操作要多一步总寻,就是要先根據(jù)篩選條件拿到要更改的對象,然后給對象賦值梢为,再次提交(commit)即可渐行。
3.5轰坊,刪除操作
程序中存在的一個對象或者已知的id編號,通過主鍵編號或者對象的任意屬性進(jìn)行數(shù)據(jù)庫中數(shù)據(jù)記錄的刪除的操作過程稱為刪除操作祟印。如管理員刪除某個會員賬號的操作肴沫,通過獲取要刪除會員的賬號,然后通過delete()函數(shù)將要刪除的會員信息告知數(shù)據(jù)庫執(zhí)行刪除操作蕴忆,數(shù)據(jù)庫中的某條存在的數(shù)據(jù)記錄就被刪除掉了樊零。
3.6,代碼整合
四孽文,查詢對象Query
Session是sqlalchemy和數(shù)據(jù)庫交互的橋梁驻襟,Session提供了一個Query對象實(shí)現(xiàn)數(shù)據(jù)庫中數(shù)據(jù)的查詢操作。
4.1芋哭,常規(guī)查詢query
直接指定類型進(jìn)行查詢
4.2沉衣,指定排序查詢
通過類型的屬性指定排序方式
4.5,指定類型別名
對于類型名稱較長的情況减牺,同樣可以指定別名進(jìn)行處理
4.6豌习,切片查詢
對于經(jīng)常用于分頁操作單額切片查詢,在使用過程中直接使用python內(nèi)置的切片即可拔疚。
1user_list = session.query(User).all()[1:3]
五肥隆,條件篩選filter
上面主要對數(shù)據(jù)查詢對象query有一個比較直觀的感受和操作,在實(shí)際使用過程中經(jīng)常用到條件查詢稚失,主要通過filter 和 filter_by 進(jìn)行操作栋艳,下面重點(diǎn)學(xué)習(xí)最為頻繁的filter條件篩選函數(shù)。
5.1句各,等值條件——equals / not equals
5.2吸占,模糊條件——like
1session.query(User).filter(User.name.like(‘%james%’))
5.3,范圍條件——in / not in
5.4凿宾,空值條件——is null / is not null
5.5矾屯,并且條件——and
5.6,或者條件——or
5.7初厚,SQL語句查詢
某些特殊的情況下件蚕,我們也可能在自己的程序中直接使用sql語句進(jìn)行操作。
5.8产禾,filter() 和 filter_by()的區(qū)別
首先看一個例子:
結(jié)果如下:
1001 James
1001 James
所以排作,從例子可以看出,filter可以像寫SQL的where條件那樣寫 < , > 等條件下愈,但引用列名時纽绍,需要通過類名蕾久,屬性名的方式势似。 filter_by可以使用python的正常參數(shù)傳遞條件拌夏,指定列名時,不需要額外指定類名履因,參數(shù)名對應(yīng)類中的屬性名障簿,不能用 < , > 等條件。
filter不支持組合查詢栅迄,只能連續(xù)調(diào)用filter變相實(shí)現(xiàn)站故,filter_by的參數(shù)是 **kwargs,直接支持組合查詢毅舆。
六西篓,查詢結(jié)果
6.1,all()函數(shù)返回查詢列表
返回一個列表憋活,可以通過遍歷列表獲取每個對象岂津。
session.query(User).all()
6.2,filter()函數(shù)返回單項數(shù)據(jù)的列表生成器
session.query(User).filter(..)
6.3悦即,one() / one_or_none() / scalar() 返回單獨(dú)的一個數(shù)據(jù)對象
one()返回且僅返回一個查詢結(jié)果吮成,當(dāng)結(jié)果數(shù)量不足或者多余一個時會報錯。
session.query(User).filter(..).one()/one_or_none()/scalar()
6.4辜梳,first() 返回一個單項結(jié)果
返回至多一個結(jié)果粱甫,而且以單項形式,而不是只有一個元素的tuple形式返回作瞄。
session.query(User).filter(User.id > 1001).first()
七茶宵,遇到的問題及其解決方法
7.1 sqlalchemy查詢中,如果沒有符合條件的結(jié)果宗挥,會返回一個空的對象节预,如何判斷這個對象是空的?
解決問題的代碼:
八属韧,擴(kuò)展知識
8.1 Python雙下劃線開頭的函數(shù)和變量
python 用下劃線作為變量前綴和后綴指定特殊變量安拟。
__XXX 不能用 ‘ from module import * ’ 導(dǎo)入
XXX 系統(tǒng)定義名稱
__XXX 類中的私有變量名
核心風(fēng)格:避免用下劃線作為變量名的開始。
因?yàn)橄聞澗€對解釋器有特殊的意義宵喂,而且是內(nèi)建標(biāo)識符所使用的符號糠赦,我們建議程序員避免使用下劃線作為變量名的開始。一般來說锅棕,變量名__XXX 被看做是“私有”的拙泽,在模塊或類外不可以使用。當(dāng)變量是私有的時候裸燎,用XXX來表示變量是很好地習(xí)慣顾瞻。因?yàn)樽兞棵?strong>XXX對python來說有特殊含義,對于普通的變量應(yīng)當(dāng)避免這種命名風(fēng)格德绿。
“單下劃線”開始的成員變量叫做保護(hù)變量荷荤,意思是只有類對象和子類對象自己能訪問到這些變量退渗;
“雙下劃線”開始的是私有成員,意思是只有類對象自己能訪問蕴纳,連子類對象也不能訪問到這個數(shù)據(jù)会油。
以單下劃線開頭(__foo)的代表不能直接訪問的類屬性,需通過類提供的接口進(jìn)行訪問古毛,不能用“from XXX import * ”而導(dǎo)入翻翩;
以雙下劃線開頭的(__foo)代表類的私有成員;
以雙下劃線開頭和結(jié)尾的(foo)代表python里特殊方法專用的標(biāo)識稻薇,如init() 代表類的構(gòu)造函數(shù)嫂冻。
8.2 if name == ''main''
所有的python模塊都是對象并且有幾個有用的屬性,你可以使用這些屬性方便的測試你所書寫的模塊塞椎。
模塊是對象絮吵,并且所有的模塊都有一個內(nèi)置屬性name。一個模塊的name的值要你您如何應(yīng)用模塊忱屑。如果import模塊蹬敲,那么name的值通常為模塊的文件名,不帶路徑或者文件擴(kuò)展名莺戒。但是我們也可以像一個標(biāo)準(zhǔn)的程序一樣直接運(yùn)行模塊伴嗡,在這種情況下name的值將會是一個特別的缺省值:main。