Sqlalchemy非同步操作不完全指北

米洛丶發表於2022-04-10

非同步SQLAlchemy

SQLAlchemy作為一款通用的Python Orm工具,在最近的版本也支援了非同步操作。但網上很多資料都不是很齊全,API也不是很好查詢的情況下,我便有了整理一份基礎文件的想法。文章主要會以CRUD為入口,解決大家最基本的需求。

engine的區別

在普通的SQLAlchemy中,建立engine物件,我們會採用下面的方式:

from sqlalchemy import create_engine
engine = create_engine(SQLALCHEMY_DATABASE_URI, pool_recycle=1500)

而非同步的方式如下:

from sqlalchemy.ext.asyncio import create_async_engine
async_engine = create_async_engine(ASYNC_SQLALCHEMY_URI, pool_recycle=1500)

session的區別

我們一般用sessionmaker來建立session,不過非同步的有點區別:

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import sessionmaker

# 同步session
Session = sessionmaker(engine)

# 非同步session 區別在於需要指定對應的class_
async_session = sessionmaker(async_engine, class_=AsyncSession)

建立會話

我們還是以程式碼的形式展示:

# 同步
with Session() as session:
  # 裡面是具體的sql操作
  pass
  
# 非同步
async with Session() as session:
    # 裡面是非同步的操作,區別就是從with變成了async with 也就意味著方法必須是async修飾的
    pass


以上是關於建立連線,處理會話的一些區別,接著我們講對應的CRUD操作。

查詢

這裡依舊會給出新老版本的對比:

# 注意Session為同步Session,為了區分,非同步session為async_session
# model則為具體的Model類

# 非同步查詢方式
from sqlalchemy import select


async def query():
    async with async_session() as session:
        sql = select(model).where(model.id == 1)
        print(sql) # 這裡可以列印出sql
        result = await session.execute(sql)
        # 第一條資料
        data = result.scalars().first()
        # 所有資料
        # data = result.scalars().all()


# 同步查詢方式一
def query():
    with Session() as session:
        # 查詢id=1的第一條資料 result對應的就是model的例項 如果沒有則是None
        result = session.query(model).filter_by(id=1).first()
        # 查詢所有資料 result對應的資料為List[model],即model陣列
        # result = session.query(model).filter_by(name="zhangsan").all()

# 同步查詢方式二
def query():
    with Session() as session:
        # 查詢id=1的第一條資料 result對應的就是model的例項 如果沒有則是None
        result = session.query(model).filter(model.id == 1).first()
        # 查詢所有資料 result對應的資料為List[model],即model陣列
        # result = session.query(model).filter(model.name == "zhangsan").all()

新增

這裡開始就只講非同步的操作了。

async def insert(data):
    async with async_session() as session:
        async with session.begin():
            session.add(data)
            # 重新整理自帶的主鍵
            await session.flush()
            # 釋放這個data資料
            session.expunge(data)
            return data

先說一下session.begin,這個你可以理解為一個事務操作,當採用session的begin方法後,你可以發現我們不需要呼叫commit方法也能把修改存入資料庫。

expunge方法,是用例釋放這個例項,SQLAlchemy有個特點,當你的session會話結束以後,它會銷燬你插入的這種臨時資料,你再想訪問這個data就訪問不了了。所以我們可以釋放這個資料。(expunge的作用)

編輯

一般編輯有2種方式:

  • 查詢出對應的資料,在資料上修改
  • 根據key-value的形式,修改對應資料的欄位
from sqlalchemy import select, update

# 方式一
async def update_record(model):
    async with async_session() as session:
        async with session.begin():
            result = await session.execute(select(model).where(id=1))
            now = result.scalars().first()
            if now is None:
                raise Exception("記錄不存在")
            now.name = "李四"
            now.age = 23
            # 這裡測試過,如果去掉flush會導致資料不更新
            await session.flush()
            session.expunge(now)
            return now

# 方式二
async def update_by_map():
    async with async_session() as session:
        async with session.begin():
            # 更新id為1的資料,並把name改為李四 age改為23
            sql = update(model).where(model.id == 1).values(name="李四", age=23)
            await session.execute(sql)

刪除

刪除的話,軟刪除大家都是update,所以不需要多說,物理刪除的話,也有兩種方式:

  • 查到以後刪除之
  • 直接根據條件刪除(這種我沒有仔細研究,我選的是第一種方式,容錯率高點)
async def delete_by_id():
    async with async_session() as session:
        async with session.begin():
            result = await session.execute(select(model).where(model.id == 2))
            original = result.scalars().first()
            if original is None:
                raise Exception("記錄不存在")
            # 如果是多條
            # session.delete(original)
            # for item in result:
            #     session.delete(item)

今天的非同步內容就整理到這裡,我個人覺得還是很實用的,希望對大家有幫助~~~

相關文章