Python中不使用ORM时如何更好地组织代码?
过去的项目经验一般在操作数据库这块都是使用 ORM ,
能比较好的使用一些面向对象的特性,方便习惯了
现在有一个项目需要全局使用 sql 操作数据库, 每一处的增删改查都是一句或几句 sql ,
请问下这种情况下如何组织代码更好一些,有没有什么最佳实践可供参考?
Python中不使用ORM时如何更好地组织代码?
linq 路过,微微一笑。
直接上干货。核心思路是分层,把数据访问、业务逻辑和界面展示分开。我给你一个经典的“数据访问层(DAL)+ 服务层(Service)”的简单架子。
# 1. 数据访问层 (data_access.py) - 专门负责和数据库打交道
import sqlite3
from typing import List, Optional, Tuple
class UserRepository:
def __init__(self, db_path: str):
self.db_path = db_path
def _get_connection(self):
"""获取数据库连接,这里用sqlite举例,实际可能是pymysql等"""
return sqlite3.connect(self.db_path)
def get_user_by_id(self, user_id: int) -> Optional[Tuple]:
"""根据ID查询用户,返回元组或None"""
with self._get_connection() as conn:
cursor = conn.cursor()
cursor.execute("SELECT id, name, email FROM users WHERE id = ?", (user_id,))
return cursor.fetchone()
def create_user(self, name: str, email: str) -> int:
"""创建用户,返回新用户的ID"""
with self._get_connection() as conn:
cursor = conn.cursor()
cursor.execute(
"INSERT INTO users (name, email) VALUES (?, ?)",
(name, email)
)
conn.commit()
return cursor.lastrowid
def get_users_by_name(self, name_prefix: str) -> List[Tuple]:
"""根据名称前缀查询用户列表"""
with self._get_connection() as conn:
cursor = conn.cursor()
cursor.execute(
"SELECT id, name, email FROM users WHERE name LIKE ?",
(f"{name_prefix}%",)
)
return cursor.fetchall()
# 2. 服务/业务逻辑层 (user_service.py) - 处理业务规则
class UserService:
def __init__(self, user_repo: UserRepository):
self.repo = user_repo
def register_new_user(self, name: str, email: str) -> dict:
"""注册新用户(这里可以添加业务逻辑,比如检查邮箱格式)"""
if not email or "@" not in email:
raise ValueError("Invalid email address")
# 调用数据访问层
user_id = self.repo.create_user(name, email)
return {"id": user_id, "name": name, "email": email, "status": "registered"}
def get_user_profile(self, user_id: int) -> Optional[dict]:
"""获取用户资料,并可以在这里整合其他数据或计算"""
user_tuple = self.repo.get_user_by_id(user_id)
if not user_tuple:
return None
# 将元组转换为更友好的字典,或在此处添加其他业务逻辑
return {
"id": user_tuple[0],
"name": user_tuple[1],
"email": user_tuple[2],
"profile_complete": bool(user_tuple[1] and user_tuple[2]) # 示例业务逻辑
}
# 3. 主程序或API层 (main.py) - 这里是入口,比如Web框架的视图函数或命令行
if __name__ == "__main__":
# 初始化
db_path = "my_database.db"
repo = UserRepository(db_path)
service = UserService(repo)
# 使用服务
try:
new_user = service.register_new_user("张三", "zhangsan@example.com")
print(f"新用户创建成功: {new_user}")
except ValueError as e:
print(f"创建失败: {e}")
user_profile = service.get_user_profile(1)
if user_profile:
print(f"用户资料: {user_profile}")
关键点:
- Repository模式(DAL层):所有SQL语句和数据库操作封装在这里。如果要换数据库,基本上只改这一层。
- Service层:放你的业务逻辑(验证、计算、流程控制)。它调用Repository,但不关心数据怎么存。
- 依赖注入:像上面那样,Service层接收一个Repository实例。这样方便测试(你可以传一个模拟的Repository进去)。
好处是:代码立马变清晰了。SQL归SQL,业务归业务。以后加缓存、改数据库、换接口,动一块地方就行,不会牵一发而动全身。
总结:按功能分层,各司其职。
封装个数据库操作类吧,再进一步封装就又成 orm 了
5999
将 sql 存到配置文件或数据库,不要写到代码里。
存储过程
Mybatis 不就是直接调用 SQL 语句?
当然 Mybatis 不是 python 的框架,可以用来参考,很多情况下直接使用 SQL 语句比 ORM 还更方便一些,由于是业务逻辑比较复杂的情况下。
记得在 sf.gg 就见到过有人说: orm 本来就是为了顺应面向对象而出的失败产物, orm 对于一些关联表和自联表(比如说无限极分类)完全不能按照正常人的思维来封装和理解。
所以综上所述,哪个方便哪个来。
我个人觉得看有换行和格式化过的 sql 会比 orm 操作代码更加清晰明了,所以我在 laravel 和 python 里面一直用的是查询构造器而不是 orm 。
我的体会是,
ORM 好处是可以借助 IDE 来提高编码效率(毕竟都是面向对象的类与实例 IDE 看得懂,即可以在编码时更好的自动补全代码,也可以更好的自动分析代码中的潜在问题),缺点也是不用 IDE 就没意思了;
使用 sql builder 的好处是可以自由应对除普通 CRUD 外的奇怪查询需求,代码看起来也没有 ORM 那么啰嗦,缺点是维护起来会苦逼。
我的建议仍然是把语句拆分成 orm 操作。否则可维护性和开发效率太不平衡了。你用任何方式封装大量的操作,最后会发现,他都是个 orm ar 之类的东西。
python 有啥好用的构造器推荐不
总是手工 SQL 语句的路过.
sqlalchemy sql expression
我们 erp 的报表都是用这个的, 维护性很高(直接用 sql 根本无法维护)
存储过程
使用代码生成
用 Node.JS / TypeScript 的话可以考虑用 Schemats , https://github.com/SweetIQ/schemats
http://cs.mcgill.ca/~mxia3/2016/11/18/Statically-typed-PostgreSQL-queries-and-typescript-schemats/


