簡(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ù)
- 方式一,多參數(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()
- 方式二洞拨,與語(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