SQLAlchemy 入門(mén)教程

SQLAlchemy 入門(mén)教程

前言

目前筐钟,許多主流的語(yǔ)言,都實(shí)現(xiàn)了對(duì)象關(guān)系映射(Object Relational Mapper赋朦,簡(jiǎn)稱ORM)的庫(kù)包篓冲。ORM的主要功能是將數(shù)據(jù)庫(kù)表中的每條記錄映射成一個(gè)對(duì)象。所有的數(shù)據(jù)庫(kù)操作宠哄,都轉(zhuǎn)化為對(duì)象的操作壹将。這樣可以增加代碼的可讀性和安全性。

ORM優(yōu)點(diǎn):

  1. 簡(jiǎn)潔易讀:將數(shù)據(jù)表抽象為對(duì)象(數(shù)據(jù)模型)琳拨,更直觀易讀瞭恰。
  2. 可移植:封裝了多種數(shù)據(jù)庫(kù)引擎屯曹,面對(duì)多個(gè)數(shù)據(jù)庫(kù)狱庇,操作基本一致,代碼易維護(hù)恶耽。
  3. 更安全:有效避免SQL注入密任。

當(dāng)然性能上會(huì)低于直接執(zhí)行SQL語(yǔ)句,本文介紹SQLAlchemy的一些基礎(chǔ)操作偷俭。

1. 建立連接

任何SQLAlchemy應(yīng)用程序的開(kāi)始都是一個(gè)名為engine . 此對(duì)象充當(dāng)連接到特定數(shù)據(jù)庫(kù)的中心源浪讳,提供工廠和稱為 connection pool對(duì)于這些數(shù)據(jù)庫(kù)連接。引擎通常是一個(gè)只為特定數(shù)據(jù)庫(kù)服務(wù)器創(chuàng)建一次的全局對(duì)象涌萤,并使用一個(gè)URL字符串進(jìn)行配置淹遵,該字符串將描述如何連接到數(shù)據(jù)庫(kù)主機(jī)或后端口猜。

# dialect[+driver]://user:password@host/dbname[?key=value..]
engine = create_engine("mysql://scott:tiger@hostname/dbname", encoding='latin1', echo=True)

創(chuàng)建engineURL格式為dialect[+driver]://user:password@host/dbname[?key=value..],其中dialect表示數(shù)據(jù)庫(kù)類型例如:mysql透揣、oracle济炎、postgresql等,而driver代表使用的數(shù)據(jù)庫(kù)API如:psycopg2辐真、pyodbc等须尚。

  • create_engine.echo:設(shè)置為True時(shí)會(huì)打印日志∈淘郏可以查看調(diào)用的具體SQL語(yǔ)句方便調(diào)試耐床。

  • create_engine.future :標(biāo)志設(shè)置為 True 以便我們充分利用 2.0 style1.x --> 2.0最主要的API更改是從select()改使用Query對(duì)象楔脯。

  • create_engine.pool_size:連接池的大小默認(rèn)為5個(gè)撩轰,設(shè)置為0時(shí)表示連接無(wú)限制。

  • create_engine.pool_recycle:設(shè)置時(shí)間以限制數(shù)據(jù)庫(kù)多久沒(méi)連接自動(dòng)斷開(kāi)昧廷。

# python3 下使用pymysql連接mysql钧敞。
engine = create_engine(
  "mysql+pymysql://user:pwd@host/dbname", 
  echo=True,
  pool_size=8,
  pool_recycle=3600
)
# 連接SQLite
engine = create_engine("sqlite+pysqlite:///:memory:", echo=True, future=True)

2. ORM的會(huì)話

使用ORM時(shí),基本的事務(wù)/數(shù)據(jù)庫(kù)交互對(duì)象稱為Session麸粮。

SQLAlchemy中溉苛,這個(gè)對(duì)象通常傳遞我們給它的SQL語(yǔ)句,它管理ORM映射對(duì)象的持久性操作弄诲。

Session的主要目的是建立與數(shù)據(jù)庫(kù)的會(huì)話愚战,它維護(hù)你加載和關(guān)聯(lián)的所有數(shù)據(jù)庫(kù)對(duì)象。它是數(shù)據(jù)庫(kù)查詢Query的一個(gè)入口齐遵。

Session通常在我們需要對(duì)數(shù)據(jù)庫(kù)進(jìn)行操作時(shí)創(chuàng)建寂玲。

一旦一個(gè)Session創(chuàng)建成功,我們?cè)谶@個(gè)Session下完成多個(gè)事務(wù)(transaction)梗摇。

究竟何時(shí)創(chuàng)建和關(guān)閉Session拓哟,不能一概而論,但是一個(gè)原則是Session不應(yīng)該在操作事務(wù)的方法中創(chuàng)建伶授。

sessionmaker函數(shù)是配置Session的工廠断序,通過(guò)它建立的配置參數(shù)創(chuàng)建產(chǎn)生新的Session

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

some_engine = create_engine('mysql+pymysql://username:password@localhost/mydb?charset=utf8')
ession = sessionmaker(bind=some_engine)
session = Session()

Session的常見(jiàn)操作方法包括:

  • flush:預(yù)提交糜烹,提交到數(shù)據(jù)庫(kù)文件违诗,還未寫(xiě)入數(shù)據(jù)庫(kù)文件中。

  • commit:提交了一個(gè)事務(wù)疮蹦。

  • rollback:回滾诸迟。

  • close:關(guān)閉會(huì)話。

3. 創(chuàng)建模型

前面有提到ORM的重要特點(diǎn),那么我們操作表的時(shí)候就需要通過(guò)操作對(duì)象來(lái)實(shí)現(xiàn)阵苇,現(xiàn)在我們來(lái)創(chuàng)建一個(gè)類壁公,以常見(jiàn)的用戶表舉例:

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, String, Integer, SMALLINT, DECIMAL, Enum, TEXT, TIMESTAMP
Base = declarative_base()

class Users(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True)
    name = Column(String(64), unique=True)
    email = Column(String(64))

    def __init__(self, name, email):
        self.name = name
        self.email = email

declarative_base()sqlalchemy內(nèi)部封裝的一個(gè)方法,通過(guò)其構(gòu)造一個(gè)基類绅项,這個(gè)基類和它的子類贮尖,可以將Python類和數(shù)據(jù)庫(kù)表關(guān)聯(lián)映射起來(lái)。

數(shù)據(jù)庫(kù)表模型類通過(guò)__tablename__和表關(guān)聯(lián)起來(lái)趁怔,Column表示數(shù)據(jù)表的列湿硝。

4. 生成表

Base.metadata.create_all(engine)

創(chuàng)建表,如果存在則忽略润努,執(zhí)行以上代碼关斜,就會(huì)發(fā)現(xiàn)在數(shù)據(jù)庫(kù)中創(chuàng)建了users表。

5. 抽象模型

現(xiàn)在我們修改之前的用戶表铺浇,加入新字段和一張訂單表痢畜。

使用了一個(gè)基類,定義了共同的字段:id和創(chuàng)建更新時(shí)間鳍侣。值得注意的是基類中設(shè)置了__abstract__如果不設(shè)置將會(huì)報(bào)錯(cuò)丁稀。

order中定義了一個(gè)枚舉類型,同時(shí)orderuser使用user_id進(jìn)行關(guān)聯(lián)并未使用外鍵倚聚。

import enum
from sqlalchemy import Column, String, Integer, SMALLINT, FLOAT, DECIMAL, Enum
from sqlalchemy.sql.functions import current_timestamp, current_time
from orm.base_model import BaseTimestampModel
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class PayType(enum.Enum):

    alipay = 0
    unionpay = 1
    weixin = 2
    balance = 3
    combo = 4
    package = 5
    company = 6
    offline = 7

class BaseModel(Base):
    __abstract__ = True
    id = Column(Integer, primary_key=True, autoincrement=True)
    created_at = Column(TIMESTAMP, nullable=False, default=current_timestamp())
    updated_at = Column(TIMESTAMP, nullable=False, default=current_timestamp()

class User(BaseModel):

    __tablename__ = 'user'
    mobile = Column(String)
    nickname = Column(String)
    status = Column(SMALLINT, default=1)
    appid = Column(String, nullable=True)

    def __init__(self, nickname, mobile):
        self.nickname = nickname
        self.mobile = mobile                    


class Order(BaseModel):

    __tablename__ = 'order'
    user_id = Column(Integer)                    
    ordersn = Column(String, unique=True)                        
    order_type = Column(SMALLINT, default=2)
    pay_type = Column(Enum(PayType), default=PayType.alipay)
    price = Column(DECIMAL)      
              

6. 新增數(shù)據(jù)

add_user = User("test", "1351232322")
session.add(add_user)
session.commit()

session.add()將會(huì)把Model加入當(dāng)前session維護(hù)的持久空間(可以從session.dirty看到)中线衫,直到commit時(shí)提交到數(shù)據(jù)庫(kù)。

  • Q1:add之后如何直接返回對(duì)象的屬性惑折?

可以在add之后執(zhí)行db.session.flush()授账,這樣便可在sessionget到對(duì)象的屬性。

  • Q2:如何進(jìn)行批量插入惨驶,性能比較白热?

批量插入共有以下幾種方法,對(duì)它們的批量做了比較粗卜,分別是:

session.add_all() < bulk_save_object()< bulk_insert_mappings() < SQLAlchemy_core()

7. 查詢數(shù)據(jù)

查詢是最常用的一個(gè)操作了屋确,舉個(gè)最簡(jiǎn)單的查詢例子:

# 檢索id = 1
user = session.query(Users).filter(id == 1).first()
# 檢索全部用戶
users = session.query(Users).all()

使用Join查詢

# 未使用外鍵
q = session.query(User).join(Order, User.id==Order.user_id)
# 方法2
q = session.query(User).join(Order, Order.user_id)
# 配置了外鍵
q = session.query(User).join(Order)

下一個(gè)例子使用SQL中的函數(shù)和Left join,檢索當(dāng)日下單用戶昵稱和訂單信息续扔。

from sqlalchemy.sql.functions import current_date

result = session.query(Order, User.nickname).outerjoin(
            User, User.id == Order.user_id
        ).filter(
            Order.order_type != 2,
            sqlalchemy.func.date(Order.created_at) == current_date
        ).all()

使用 in 攻臀、or查詢語(yǔ)句。

# 檢索id在[1, 2, 3]中的用戶测砂。
r = session.query(User).filter(User.id.in_([1,2,3])).all()
# 使用or
query = bus_session.query(User)
filters = [User.id.in_([1, 2, 3]), User.nickname.ilike("ab%")]
r = query.filter(sqlalchemy.or_(*filters)).all()

8. 更新數(shù)據(jù)

更新數(shù)據(jù)有兩種方法茵烈,一種是使用query中的update方法:

session.query(User).filter_by(id=1).update({'name': "Jack"})

另一種是操作對(duì)應(yīng)的表模型:

user = session.query(User).filter_by(name="Jack").first()
user.name = "test"
session.add(users)
session.commit()

9. 刪除數(shù)據(jù)

和更新數(shù)據(jù)類似百匆,刪除數(shù)據(jù)也有兩種方法砌些,第一種:

user = session.query(User).filter(User.name == "test").first()
session.delete(user)
session.commit()

批量刪除的時(shí)候建議使用第二種:

session.query(User).filter(User.name == "test").delete()
session.commit()

參考資料

  1. SQLAlchemy 1.4 / 2.0 Tutorial

  2. SQLAlchemy ORM Examples

  3. python SQLAlchemy入門(mén)教程

  4. sqlalchemy學(xué)習(xí)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子存璃,更是在濱河造成了極大的恐慌仑荐,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纵东,死亡現(xiàn)場(chǎng)離奇詭異粘招,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)偎球,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)洒扎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人衰絮,你說(shuō)我怎么就攤上這事袍冷。” “怎么了猫牡?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵胡诗,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我淌友,道長(zhǎng)煌恢,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任震庭,我火速辦了婚禮瑰抵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘器联。我一直安慰自己谍憔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布主籍。 她就那樣靜靜地躺著习贫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪千元。 梳的紋絲不亂的頭發(fā)上苫昌,一...
    開(kāi)封第一講書(shū)人閱讀 49,046評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音幸海,去河邊找鬼祟身。 笑死,一個(gè)胖子當(dāng)著我的面吹牛物独,可吹牛的內(nèi)容都是我干的袜硫。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼挡篓,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼婉陷!你這毒婦竟也來(lái)了帚称?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤秽澳,失蹤者是張志新(化名)和其女友劉穎闯睹,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體担神,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡楼吃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了妄讯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片孩锡。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖亥贸,靈堂內(nèi)的尸體忽然破棺而出浮创,到底是詐尸還是另有隱情,我是刑警寧澤砌函,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布斩披,位于F島的核電站,受9級(jí)特大地震影響讹俊,放射性物質(zhì)發(fā)生泄漏垦沉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一仍劈、第九天 我趴在偏房一處隱蔽的房頂上張望厕倍。 院中可真熱鬧,春花似錦贩疙、人聲如沸讹弯。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)组民。三九已至,卻和暖如春悲靴,著一層夾襖步出監(jiān)牢的瞬間臭胜,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工癞尚, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留耸三,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓浇揩,卻偏偏與公主長(zhǎng)得像仪壮,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子胳徽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345