Python开发中,SQLAlchemy 的同步操作和异步操作封装,以及常规CRUD的处理。
1、SQLAlchemy介绍
SQLAlchemy 允许开发者通过 Python 代码与数据库进行交互,而无需直接编写 SQL 语句,同时也支持直接使用原生 SQL 进行复杂查询。下面是SQLAlchemy和我们常规数据库对象的对应关系说明。1)数据库表 (Database Table)
- SQLAlchemy: 使用
Table对象或Declarative Base中的类来表示。 - 对应关系: 数据库中的每一个表对应于SQLAlchemy中的一个类,该类继承自
declarative_base()。
from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class User(Base):
__tablename__ = 'users' # 数据库表名
id = Column(Integer, primary_key=True)
name = Column(String)
email = Column(String)
2)数据库列 (Database Column)
- SQLAlchemy: 使用
Column对象来表示。 - 对应关系: 每个数据库表中的列在SQLAlchemy中表示为
Column对象,并作为类的属性定义。
id = Column(Integer, primary_key=True)
name = Column(String(50))
3)数据库行 (Database Row)
- SQLAlchemy: 每个数据库表的一个实例(对象)代表数据库表中的一行。
- 对应关系: 在SQLAlchemy中,通过实例化模型类来表示数据库表中的一行。
new_user = User(id=1, name='John Doe', email='john@example.com')
4)主键 (Primary Key)
- SQLAlchemy: 使用
primary_key=True参数定义主键。 - 对应关系: 在数据库表中定义主键列,这列在SQLAlchemy中也需要明确标注。
id = Column(Integer, primary_key=True)
5)外键 (Foreign Key)
- SQLAlchemy: 使用
ForeignKey对象来表示。 - 对应关系: 在SQLAlchemy中使用
ForeignKey指定关系,指向另一个表的主键列。
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship class Address(Base):
__tablename__ = 'addresses'
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('users.id'))
user = relationship('User')
6)关系 (Relationships)
- SQLAlchemy: 使用
relationship对象来表示。 - 对应关系: 数据库中表与表之间的关系在SQLAlchemy中通过
relationship来定义。
addresses = relationship("Address", back_populates="user")
7)会话 (Session)
- SQLAlchemy: 使用
Session对象进行事务性操作(如查询、插入、更新、删除)。 - 对应关系:
Session对象类似于数据库连接对象,用于与数据库进行交互。
from sqlalchemy.orm import sessionmaker Session = sessionmaker(bind=engine)
session = Session() session.add(new_user)
session.commit()
通过以上对应关系,SQLAlchemy允许开发者以面向对象的方式与数据库交互,提供了一个Pythonic的接口来操作数据库。
2、SQLAlchemy 的同步操作
SQLAlchemy 提供了同步和异步两种操作方式,分别适用于不同的应用场景。以下是如何封装 SQLAlchemy 的同步和异步操作的方法说明:
在同步操作中,SQLAlchemy 使用传统的阻塞方式进行数据库操作。首先,定义一个基础的 Session 和 Engine 对象:
from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base, sessionmaker
from typing import Generator
from core.config import settings # 常规同步处理
engine = create_engine(settings.DB_URI)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) def get_db() -> Generator:
"""创建一个 SQLAlchemy 数据库会话-同步处理."""
try:
db = SessionLocal()
yield db
finally:
db.close()
前面说了,使用SQLAlchemy可以实现不同数据库的统一模型的处理,我们可以对应创建不同数据库的连接(engine),如下是常规几种关系型数据库的连接处理。
# mysql 数据库引擎
engine = create_engine(
"mysql+pymysql://root:123456@127.0.0.1:3306/WinFramework",
pool_recycle=3600,
# echo=True,
) # Sqlite 数据库引擎
engine = create_engine("sqlite:///testdir//test.db") # PostgreSQL 数据库引擎
engine = create_engine(
"postgresql+psycopg2://postgres:123456@localhost:5432/winframework",
# echo=True,
) # SQLServer 数据库引擎
engine = create_engine(
"mssql+pymssql://sa:123456@localhost/WinFramework?tds_version=7.0",
# echo=True,
)
我们可以根据数据库的CRUD操作方式,封装一些操作,如下所示。
class CRUDOperations:
def __init__(self, model):
self.model = model def create(self, db, obj_in):
db_obj = self.model(**obj_in.dict())
db.add(db_obj)
db.commit()
db.refresh(db_obj)
return db_obj def get(self, db, id):
return db.query(self.model).filter(self.model.id == id).first() def update(self, db, db_obj, obj_in):
obj_data = obj_in.dict(exclude_unset=True)
for field in obj_data:
setattr(db_obj, field, obj_data[field])
db.commit()
db.refresh(db_obj)
return db_obj def remove(self, db, id):
obj = db.query(self.model).get(id)
db.delete(obj)
db.commit()
return obj
使用时,构建数据访问类进行操作,如下测试代码所示。
crud_user = CRUDOperations(User) # Create
with get_db() as db:
user = crud_user.create(db, user_data) # Read
with get_db() as db:
user = crud_user.get(db, user_id) # Update
with get_db() as db:
user = crud_user.update(db, user, user_data) # Delete
with get_db() as db:
crud_user.remove(db, user_id)
3、SQLAlchemy 的异步操作封装
对于异步操作,SQLAlchemy 使用 AsyncSession 来管理异步事务。
首先,定义一个异步的 Session 和 Engine 对象:
from sqlalchemy import create_engine, URL
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from typing import AsyncGenerator def create_engine_and_session(url: str | URL):
try:
# 数据库引擎
engine = create_async_engine(url, pool_pre_ping=True)
except Exception as e:
print(" 数据库链接失败 {}", e)
sys.exit()
else:
db_session = async_sessionmaker(
bind=engine, autoflush=False, expire_on_commit=False
)
return engine, db_session # 异步处理
async_engine, async_session = create_engine_and_session(settings.DB_URI_ASYNC) async def get_db() -> AsyncGenerator[AsyncSession, None]:
"""创建一个 SQLAlchemy 数据库会话-异步处理."""
async with async_session() as session:
yield session
和同步的处理类似,不过是换了一个对象来实现,并且函数使用了async await的组合来实现异步操作。
为了实现我的SQLSugar开发框架类似的封装模式,我们参考SQLSugar开发框架中基类CRUD的定义方式来实现多种接口的封装处理。

参照上面的实现方式,我们来看看Python中使用泛型的处理封装类的代码。
ModelType = TypeVar("ModelType", bound=Base)
PrimaryKeyType = TypeVar("PrimaryKeyType", int, str, float) # 限定主键的类型
PageDtoType = TypeVar("PageDtoType", bound=BaseModel)
DtoType = TypeVar("DtoType", bound=BaseModel)
class BaseCrud(Generic[ModelType, PrimaryKeyType, PageDtoType, DtoType]):
"""
基础CRUD操作类
"""
def __init__(self, model: Type[ModelType]):
"""
数据库访问操作的基类对象(CRUD).
**Parameters**
* `model`: A SQLAlchemy model class
"""
self.model = model
这样,我们就可以通过泛型定义不同的类型,以及相关的处理类的信息。
该基类函数中,异步定义get_all的返回所有的数据接口如下所示。
async def get_all(
self, sorting: Optional[str], db: AsyncSession
) -> List[ModelType] | None:
"""根据ID字符串列表获取对象列表 :param sorting: 格式:name asc 或 name asc,age desc
"""
query = select(self.model)
if sorting:
query = self.apply_sorting(query, sorting) result = await db.execute(query)
items = result.scalars().all()
return items
而对应获得单个对象的操作函数,如下所示。
async def get(self, id: PrimaryKeyType, db: AsyncSession) -> Optional[ModelType]:
"""根据主键获取一个对象"""
query = select(self.model).filter(self.model.id == id) result = await db.execute(query)
item = result.scalars().first() return item
而创建对象的操作函数,如下所示。
async def create(self, obj_in: DtoType, db: AsyncSession, **kwargs) -> bool:
"""创建对象,使用 kwargs 时可以扩展创建对象时的字段。 :param obj_in: 对象输入数据
:param kwargs: 扩展字段,如格式: is_deleted=0, is_active=1
"""
try:
if kwargs:
instance = self.model(**obj_in.model_dump(), **kwargs)
else:
instance = self.model(**obj_in.model_dump()) # type: ignore db.add(instance)
await db.commit()
return True
except SQLAlchemyError as e:
print(e)
await db.rollback()
return False
这个异步函数 create 旨在通过 SQLAlchemy 在数据库中创建一个对象,同时允许通过 kwargs 参数动态扩展创建对象时的字段。
async def: 表明这是一个异步函数,可以与await一起使用。self: 这是一个类的方法,因此self引用类的实例。obj_in: DtoType:obj_in是一个数据传输对象(DTO),它包含了需要插入到数据库中的数据。DtoType是一个泛型类型,用于表示 DTO 对象。db: AsyncSession:db是一个 SQLAlchemy 的异步会话(AsyncSession),用于与数据库进行交互。**kwargs: 接受任意数量的关键字参数,允许在对象创建时动态传入额外的字段。
obj_in.model_dump(): 假设obj_in是一个 Pydantic 模型或类似结构,它可以通过model_dump()方法转换为字典格式,用于创建 SQLAlchemy 模型实例。self.model(**obj_in.model_dump(), **kwargs): 使用obj_in中的字段以及通过kwargs传入的扩展字段来实例化 SQLAlchemy 模型对象。如果kwargs非空,它们会被解包并作为额外的字段传入模型构造函数。
db.add(instance): 将新创建的对象添加到当前的数据库会话中。await db.commit(): 提交事务,将新对象保存到数据库。
SQLAlchemyError: 捕获所有 SQLAlchemy 相关的错误。await db.rollback(): 在发生异常时,回滚事务,以防止不完整或错误的数据被提交。
通过上面的封装,我们可以测试调用的处理例子
from crud.customer import customer as customer_crud
from models.customer import Customer
from pydantic import BaseModel
from schemas.customer import CustomerDto, CustomerPageDto async def test_list_customer():
async with get_db() as db: print("get_list")
totalCount, items = await customer_crud.get_list(
CustomerPageDto(skipCount=0, maxResultCount=10, name="test"),
db,
)
print(totalCount, items)
for customer in customers:
print(customer.name, customer.age) print("get_by_name")
name = "test"
customer = await customer_crud.get_by_name(
name,
db,
)
if customer:
print(customer.name, customer.age)
else:
print(f"{name} not found") print("soft delete")
result = await customer_crud.delete_byid(customer.id, db, is_deleted=1)
print("操作结果:", result) print("soft delete_byids")
result = await customer_crud.delete_byids(
["11122", "2C5F8672-2AA7-4B14-85AD-DF56F5BF7F1F"], db, is_deleted=1
)
print(f"Soft delete successful: {result}") print("update_by_column")
result = await customer_crud.update_by_column(
"id", customer.id, {"age": 30}, db
)
print("操作结果:", result) await db.close()
同步和异步处理的差异:
- 同步操作 适用于传统的阻塞式应用场景,比如命令行工具或简单的脚本。
- 异步操作 更适合异步框架如
FastAPI,可以提高高并发场景下的性能。
通过封装数据库操作,可以让代码更具复用性和可维护性,支持不同类型的操作场景。
Python开发中,SQLAlchemy 的同步操作和异步操作封装,以及常规CRUD的处理。的更多相关文章
- Python面向对象中的继承、多态和封装
Python面向对象中的继承.多态和封装 一.面向对象的三大特性 封装:把很多数据封装到⼀个对象中,把固定功能的代码封装到⼀个代码块, 函数,对象, 打包成模块. 这都属于封装思想. 继承:⼦类可以⾃ ...
- angular开发中对请求数据层的封装
代码地址如下:http://www.demodashi.com/demo/11481.html 一.本章节仅仅是对angular4项目开发中数据请求封装到model中 仅仅是在项目angular4项目 ...
- Hybrid混合开发中schema协议的使用与封装
混合开发中JS与APP通信的实现原理: JS通过schema协议,传递参数和全局回调函数给APP端 APP执行完(如微信扫一扫)后,调用协议中传入的回调函数,在前端执行回调处理. 默认写法: < ...
- python开发中常用的框架
以下是15个最受欢迎的Python开源框架.这些框架包括事件I/O,OLAP,Web开发,高性能网络通信,测试,爬虫等. Django: Python Web应用开发框架 Django 应该是最出名的 ...
- 解决Python开发中,Pycharm中无法使用中文输入法问题
Pycharm是开发Python程序的利器,但有时会遇到无法输入中文的情况.表现为:在Ubuntu系统可以正常输入中文,却在Pycharm内写注释的时候,切换不出中文.下面演示如何解决此问题. 1.在 ...
- python开发中常见的小坑
(1)可变参数类型作为函数参数默认值,函数参数默认值的设置在Python中只会被执行一次,也就是定义该函数的时候. 解决办法,设置为None,然后判断 (2)Python中的变量名解析遵循所谓的LEG ...
- python开发中容易犯的错误整合
写在前面 长期更新的博文.多数是一些比较隐蔽的问题.欢迎留言补充. pip并不是那么安逸 pip安装对于开发者来说确实是一种解放.可以自动安装依赖包,但执行最简单的pip安装命令时,并不是所有的依赖都 ...
- Python 开发中easy_install的安装及使用
easy_install是一个python的扩展包,主要是用来简化python安装第三方安装包,在安装了easy_install之后,安装python第三方安装包就只需要在命令行中输入:easy_in ...
- 饮冰三年-人工智能-Python-30 python开发中常见的错误
1:触发条件:创建的实体类生成到数据库表时报错 报错信息:TypeError: __init__() missing 1 required positional argument: 'on_delet ...
- python语言中的编码问题(续)
上文提到了python开发中非常重要的两处设置. 一个是编解码器的默认设置defaultencoding >>> import sys >>> sys.getdef ...
随机推荐
- tcp_tw_reuse、tcp_tw_recycle、tcp_fin_timeout参数介绍
参数介绍 net.ipv4.tcp_tw_reuse = 1 表示开启重用.允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭: net.ipv4.tcp_tw_rec ...
- 如何在有数BI中实现千人千面的数据推送?
问题背景 前几天有个项目管理的同学来咨询我一个问题,该项目有一个项目进度信息表,表中有项目名称,项目阶段,项目状态,项目任务等字段,在实际工作中想要实现如下场景: 当项目名称为A时,且项目阶段是需求阶 ...
- WSS SSL HTTPS之间的关系
ssl: secure socket layer 安全套接层,简单来说是一种加密技术,通过它可以在通信的双方上建立一个安全的通信链路,因此数据交互的双方可以安全地通信,而不用担心数据被窃取:wss: ...
- 番外篇: go语言写的简要数据同步工具
go-etl工具 作为go-etl工具的作者,想要安利一下这个小巧的数据同步工具,它在同步百万级别的数据时表现极为优异,基本能在几分钟完成数据同步. 1.它能干什么的? go-etl是一个数据同步工具 ...
- 题解:P7482 不条理狂诗曲
题解:P7482 不条理狂诗曲 本题解借鉴 blossom_j 大佬思路,但这位大佬的题解似乎没放正确代码. 题意 对于每一个 \(a\) 的子区间 \(a_{l\dots r}\),求选择若干个不连 ...
- CF1929B Sasha and the Drawing 题解
CF1929B 题意 给定一个 \(n\times n\) 的正方形,已知正方形最多有 \(4\times n-2\) 条对角线,要求要有至少 \(k\) 条对角线经过至少一块黑色方格,求至少要将几条 ...
- 一文全解:LVM(逻辑卷管理器)
前两篇文章已经讲了关于磁盘分区和磁盘阵列的相关内容: 一文全懂:Linux磁盘分区 一文全懂:独立冗余磁盘阵列(RAID) 但是磁盘分区完后再想扩容或者缩容就比较麻烦了,甚至很多时候不能扩容或者缩容, ...
- 关于异步编程中的bind(this)
异步编程中的.bind(this)方法解决了异步执行后this指针指向全局函数的问题,主要可以通过以下两个场景加以说明:(本文所用例子基于React场景:为简便起见,仅在第一个例子中展示完整HTML代 ...
- 《最新出炉》系列入门篇-Python+Playwright自动化测试-53- 处理面包屑(详细教程)
1.简介 面包屑(Breadcrumb),又称面包屑导航(BreadcrumbNavigation)这个概念来自童话故事"汉赛尔和格莱特",当汉赛尔和格莱特穿过森林时,不小心迷路了 ...
- 解决Prism中对话框服务中对话框开启时回调函数不会触发的问题
解决办法 新建一个类DialogServiceExtend,然后在再注册 public class DialogServiceExtend : DialogService { public Dialo ...