sqlalchemy2.0使用記錄

簡(jiǎn)介

SQLAlchemy 實(shí)際上它分為兩部分——底層的 Core 和上層的傳統(tǒng) ORM。

  • Core 是SQLAlchemy作為“數(shù)據(jù)庫(kù)工具包”的基礎(chǔ)架構(gòu)竖幔。該庫(kù)提供了用于管理與數(shù)據(jù)庫(kù)的連接、與數(shù)據(jù)庫(kù)查詢和結(jié)果交互以及SQL語(yǔ)句的編程構(gòu)造的工具挺尿。 ORM 以核心為基礎(chǔ)提供可選 對(duì)象關(guān)系映射 能力趴酣。
  • ORM提供了一個(gè)附加的配置層,允許用戶定義的Python類 映射 以及一種稱為 會(huì)話 . 然后瓦呼,它擴(kuò)展了核心級(jí)別的SQL表達(dá)式語(yǔ)言,允許按照用戶定義的對(duì)象組合和調(diào)用SQL查詢测暗。

如何選擇央串?

  • 雖然你使用的框架中已經(jīng)內(nèi)置了ORM,但是希望添加更強(qiáng)大的報(bào)表功能碗啄,請(qǐng)選用Core质和。
  • 如果你想在一個(gè)一模式為中心的視圖中查看數(shù)據(jù)(用戶類似于SQL),請(qǐng)使用Core稚字。
  • 如果你的數(shù)據(jù)不需要業(yè)務(wù)對(duì)象饲宿,請(qǐng)使用Core。
  • 如果你要把數(shù)據(jù)看作業(yè)務(wù)對(duì)象胆描,請(qǐng)使用ORM瘫想。
  • 如果你想快速創(chuàng)建原型,請(qǐng)使用ORM昌讲。
  • 如果你需要同事使用業(yè)務(wù)對(duì)象和其他與問題域無關(guān)的數(shù)據(jù)国夜,請(qǐng)組合使用Core和ORM。

創(chuàng)建鏈接

from sqlalchemy import create_engine

engine = create_engine(
    "mysql://user:password@localhost:3306/dbname",
    echo=True,  # echo 設(shè)為 true 會(huì)打印出實(shí)際執(zhí)行的 sql剧蚣,調(diào)試的時(shí)候更方便
    future=True,  # 使用 2.0API支竹,向后兼容
    pool_size=5, # 連接池的大小默認(rèn)為 5 個(gè),設(shè)置為 0 時(shí)表示連接無限制
    pool_recycle: 3600, # 設(shè)置時(shí)間以限制數(shù)據(jù)庫(kù)多久沒連接自動(dòng)斷開鸠按。
)

Core的使用方式

使用text礼搁、select、insert目尖、update和delete等構(gòu)造SQL馒吴,再由execute執(zhí)行。實(shí)踐中推薦SQL與SQL執(zhí)行分開瑟曲,尤其是較為復(fù)雜的SQL饮戳。

使用純SQL

  • 無參數(shù)
stmt=text("select * from db_name")
with engine.connect() as conn:
     result = conn.execute(stmt)
     print(result.all())
  • 有參數(shù)
  1. 方式一,多參數(shù)
with engine.connect() as conn:
    stmt=text("INSERT INTO some_table (x, y) VALUES (:x, :y)")
    params=[{"x": 1, "y": 1}, {"x": 2, "y": 4}]
    conn.execute(stmt,params)
    conn.commit()
  1. 方式二洞拨,與語(yǔ)句綁定
with engine.connect() as conn:
    stmt=text("INSERT INTO some_table (x, y) VALUES (:x, :y)").bindparams([{"x": 1, "y": 1}, {"x": 2, "y": 4}])
    conn.execute(stmt)
    conn.commit()

方式風(fēng)格

  • 邊做邊做
with engine.connect() as conn:
    stmt=...
    conn.execute(stmt)
  • 開始一次(推薦)

此方法將同時(shí)管理 Connection并在事務(wù)結(jié)束時(shí)使用COMMIT將事務(wù)內(nèi)部的所有內(nèi)容括起來扯罐,假設(shè)塊成功,或者在異常引發(fā)時(shí)回滾

with engine.begin() as conn:
    stmt=text("INSERT INTO some_table (x, y) VALUES (:x, :y)").bindparams([{"x": 1, "y": 1}, {"x": 2, "y": 4}])
    conn.execute(stmt)

獲取與使用查詢結(jié)果

execute()函數(shù)的返回值是一熱ResultProxy對(duì)象烦衣,它允許使用索引歹河、名稱或Column對(duì)象進(jìn)行訪問。

  • 元組賦值花吟,即解包秸歧,在接收到變量時(shí)將變量按位置分配給每一行
result = conn.execute(text("select x, y from some_table"))
for x, y in result:
    .....
  • 屬性名稱,元組具有與每個(gè)列的名稱相匹配的動(dòng)態(tài)屬性名。這些名稱通常是SQL語(yǔ)句為每行中的列指定的名稱
result = conn.execute(text("select x, y from some_table"))

for row in result:
    y = row.y

    # illustrate use with Python f-strings
    print(f"Row: {row.x} {row.y}")
  • 屬性名稱與模型結(jié)合衅澈,即結(jié)果類型映射键菱,個(gè)人更喜歡這種方式,編程效率會(huì)更高
result = conn.execute(text("select x, y from some_table"))
result:[TableModel]=result.fetchall()
for row in result:
    y = row.y

    # illustrate use with Python f-strings
    print(f"Row: {row.x} {row.y}")
  • 整數(shù)索引今布,不推薦

使用ORM會(huì)話執(zhí)行Session

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

>>> from sqlalchemy.orm import Session

>>> stmt = text("SELECT x, y FROM some_table WHERE y > :y ORDER BY x, y").bindparams(y=6)
>>> with Session(engine) as session:
...     result = session.execute(stmt)
...     for row in result:
...        print(f"x: {row.x}  y: {row.y}")

映射Python類

在 1.4 版更改: 聲明性映射和經(jīng)典映射現(xiàn)在被稱為“聲明性”和“命令式”映射,并且在內(nèi)部是統(tǒng)一的

  • 命令式(Table類)
student = Table('student', metadata,
            Column('id', Integer, primary_key=True),
            Column('name', String(50), ),
            Column('age', Integer),
            Column('address', String(10)),
)
  • 聲明式(模型類)
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()   # 生成模型類的基類

class User(Base):   # 模型類必須通過繼承基類來獲得metadata
    __tablename__ = 'users'   # 聲明需要映射的表名
    id = Column(Integer,primary_key=True)
    name = Column(String(20),nullable=False)

使用類模型

實(shí)踐中使聲明式會(huì)更方便些

select_stmt = select(User.id, User.name).where(User.id == 41865)
insert_stmt = insert(User).values(name='name1')
with engine.begin() as beg:
    beg.execute(select_stmt)
    beg.execute(insert_stmt)

ORM 使用方式

Session是對(duì)transcation的封裝部默,最重要的功能是實(shí)現(xiàn)原子操作弄喘。要完成數(shù)據(jù)庫(kù)查詢,就需要建立與數(shù)據(jù)庫(kù)的連接甩牺。這就需要用到Engine對(duì)象蘑志。一個(gè)Engine可能是關(guān)聯(lián)一個(gè)Session對(duì)象,也可能關(guān)聯(lián)一個(gè)數(shù)據(jù)庫(kù)表贬派。一旦任務(wù)完成 session 會(huì)將數(shù)據(jù)庫(kù) connection 交還給 pool急但。

建立session鏈接

future=True 使用2.0API

from sqlalchemy.orm import sessionmaker

DBSession = sessionmaker(bind=engine,autocommit=False, autoflush=False,
                                                  future=True)

with DBSession() as sess:
    sess.execute()

with DBSession.begin() as sess:
    sess.execute()

session的四種狀態(tài)

ORM模型很方便地將數(shù)據(jù)庫(kù)中的一條條記錄轉(zhuǎn)變成了python中的一個(gè)個(gè)對(duì)象,有時(shí)候我們會(huì)想當(dāng)然地把兩者完全等同起來搞乏,但是不要忘了波桩,兩者之間還必須有session這個(gè)中間的橋梁。因?yàn)橛衧ession在中間做控制请敦,所以必須注目對(duì)象和記錄之間一個(gè)狀態(tài)上的差別镐躲。一般而言储玫,一個(gè)數(shù)據(jù)的對(duì)象可以有四種不同的和session關(guān)聯(lián)的狀態(tài)。

from sqlalchemy.orm import sessionmaker

DBSession = sessionmaker(bind=engine)
session = DBSession()                   #創(chuàng)建session對(duì)象
frank = Person(name='Frank')    #數(shù)據(jù)對(duì)象得到創(chuàng)建萤皂,此時(shí)為Transient狀態(tài)
session.add(frank)                      #數(shù)據(jù)對(duì)象被關(guān)聯(lián)到session上撒穷,此時(shí)為Pending狀態(tài)
session.commit()                            #數(shù)據(jù)對(duì)象被推到數(shù)據(jù)庫(kù)中,此時(shí)為Persistent狀態(tài)
session.close()                             #關(guān)閉session對(duì)象
print (frank.name)                      #此時(shí)會(huì)報(bào)錯(cuò)DetachedInstanceError裆熙,因?yàn)榇藭r(shí)是Detached狀態(tài)端礼。

new_session = DBSession()
print (new_session.query(Person).get(1).name)    #可以查詢到數(shù)據(jù)
new_session.close()

增刪改查

<!--方式一SQL-->
insert_stmt = insert(User).values(name='name1')
with DBSession() as sess:
    sess.execute(insert_stmt)
    sess.commit()
    
<!--未綁定參數(shù)-->
insert_stmt2 = insert(User)
with DBSession() as sess:
    sess.execute(insert_stmt2,{'name':'name1'})
    sess.commit()
    
<!--批量-->
with DBSession() as sess:
    sess.execute(insert_stmt2,[{'name':'name1'},{'name':'name2'}])
    sess.commit()
    
<!--另一種方式begin-->
with DBSession.begin() as sess:
    sess.execute(insert_stmt2,[{'name':'name1'},{'name':'name2'}])

<!--方式二對(duì)象-->
obj=User(name='name2')
with DBSession() as sess:
    sess.add(obj)
    sess.commit()

<!--批量-->
obj=User(name='name2')
obj2=User(name='name2')
with DBSession() as sess:
    sess.add(obj)
    sess.add(obj2)
    # 或者 s.bulk_save_objects([obj,obj2])
    sess.commit()
  • 查、改和刪

2.0 API 中不再使用 query入录,而是使用 select,update,delete 來操作數(shù)據(jù)蛤奥。但query仍可使用

# session.query(User).get(42)
session.get(User, 42)

# session.query(User).all()
session.execute(select(User)).scalars().fetchall()

# session.query(User).filter_by(name="some_user").first()
session.execute(select(User).filter_by(name="some_user")).fetchone()

# session.query(User).from_statememt(text("select * from users")).a..()
session.execute(select(User).from_statement(text("selct * from users"))).scalars().all()

# session.query(User).filter(User.name == "foo").update({"fullname": "FooBar"}, synchronize_session="evaluate")
session.execute(update(User).where(User.name == "foo").values(fullname="FooBar").execute_options(synchronize_session="evaluate"))
# synchronize_session 有三種選項(xiàng): false, "fetch", "evaluate",默認(rèn)是 evaluate
# false 表示完全不更新 Python 中的對(duì)象
# fetch 表示重新從數(shù)據(jù)庫(kù)中加載一份對(duì)象
# evaluate 表示在更新數(shù)據(jù)庫(kù)的同時(shí)僚稿,也盡量在 Python 中的對(duì)象上使用同樣的操作

自動(dòng)生成模型

<!--生成映射模型凡桥,即Class模型-->
sqlacodegen --outfile=models.py mysql://root:guess@192.168.1.250:3306/test --tables teacher,student
<!--生成命令模型,即Table模型-->
sqlacodegen --outfile=models.py mysql://root:guess@192.168.1.250:3306/test --tables teacher,student ----noclasses
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蚀同,一起剝皮案震驚了整個(gè)濱河市唬血,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌唤崭,老刑警劉巖拷恨,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異谢肾,居然都是意外死亡腕侄,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門芦疏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來冕杠,“玉大人,你說我怎么就攤上這事酸茴》衷ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵薪捍,是天一觀的道長(zhǎng)笼痹。 經(jīng)常有香客問我,道長(zhǎng)酪穿,這世上最難降的妖魔是什么凳干? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮被济,結(jié)果婚禮上救赐,老公的妹妹穿的比我還像新娘。我一直安慰自己只磷,他們只是感情好经磅,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布泌绣。 她就那樣靜靜地躺著,像睡著了一般预厌。 火紅的嫁衣襯著肌膚如雪阿迈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天配乓,我揣著相機(jī)與錄音,去河邊找鬼惠毁。 笑死犹芹,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鞠绰。 我是一名探鬼主播腰埂,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蜈膨!你這毒婦竟也來了屿笼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤翁巍,失蹤者是張志新(化名)和其女友劉穎驴一,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體灶壶,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肝断,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了驰凛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胸懈。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖恰响,靈堂內(nèi)的尸體忽然破棺而出趣钱,到底是詐尸還是另有隱情,我是刑警寧澤胚宦,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布首有,位于F島的核電站,受9級(jí)特大地震影響枢劝,放射性物質(zhì)發(fā)生泄漏绞灼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一呈野、第九天 我趴在偏房一處隱蔽的房頂上張望低矮。 院中可真熱鬧,春花似錦被冒、人聲如沸军掂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蝗锥。三九已至跃洛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間终议,已是汗流浹背汇竭。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留穴张,地道東北人细燎。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像皂甘,于是被迫代替她去往敵國(guó)和親玻驻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容