两个ORM的pk初体验:Peewee VS SQLAlchemy

之前从MySQL数据库捞数据都是使用的Peewee的ORM进行的获取,同时对于获取的结果,使用其强大的insert_many方法也能快速的导入大批量的数据。

现在实习的过程中,要上线一个服务,服务中要用到数据存储,带我的师兄说peewee就别用了,sqlalchemy很香的。所以,写此文简单记录一下我对这两个orm工具的使用感受。

1 ORM工具简介

首先,ORM [1](Object-Relational Mapping),中文翻译为对象-关系映射系统,个人理解,可以将其看做是编程中的对象(Object)和关系数据库(Relational Database)的桥梁,有了ORM这个中间件,我们就可以用操作类及其属性的方式来操作数据库中的表及其字段,第二节会举一些对比例子,可以更直观的感受ORM是如何对关系数据库“动手”的。ORM目前应用于各种的面向的对象编程实战中,比如Django自带的操作数据库的方法,就是一个ORM,使用inspectdb命令就可以根据已有的数据库结构生成数据库部分的代码 [2]。ORM的对象化开发方法大大提高了开发的效率,并且可移植性更高[1]。

其次,本文的两个对比对象是Peewee和SQLAlchemy,这两个都是高star的开源ORM工具。

Peewee:一个轻量的、有表现力的ORM——支持postgresql、mysql和sqlite。用python(2.6+,3.2+)编写,内置了对sqlite、mysql和postgresql的支持,以及hstore等特殊扩展;SQLAlchemy: python-SQL工具箱和对象关系映射器。SQLAlchemy是pythonsql工具包和对象-关系映系统,它为应用程序开发人员提供了SQL的全部功能和灵活性。

原文:peewee: A small, expressive ORM -- supports postgresql, mysql and sqlite. A small, expressive orm, written in python (2.6+, 3.2+), with built-in support for sqlite, mysql and postgresql and special extensions like hstore; SQLAlchemy: The Python SQL Toolkit and Object Relational Mapper. SQLAlchemy is the Python SQL toolkit and Object Relational Mapper that gives application developers the full power and flexibility of SQL.[3]

说白了Peewee是一个轻量级的ORM,相比之下SQLAlchemy更适合做大型的工具。并且值得一提的是,SQLAlchemy目前还有积极的讨论社区和维护。从上面的简介也能看出来,“full power and flexibility”的特点确实更加适合大型的服务来使用。

2 初体验

1. 构造models.py文件

虽然两种工具都提供了使用model.py生成数据库的方式,可以很方便的用于数据库的迁移。但是平时做的最多的还是对已有的数据库进行操作。

# Peewee导出数据库模型文件
python -e mysql -u 用户名 -P -H localhost -p 3306 数据库名 >models.py

# SQLAlchemy导出数据库模型文件
sqlacodegen --outfile ./models.py mysql+pymysql://用户名:密码@localhost:3306/数据库名 

SQLAlchemy输出的数据库结构更加完整,Peewee模型的输出是不包括视图的,如果需要需要再加一个-v的命令;SQLAlchemy默认输出所有的视图、索引,除非加上“--noviews --noconstraints --noindexes”。可以看出peewee的轻量,你需要的我在输出,没说要的我就不输出了。

Peewee安全性低,在生成的models.py文件中,peewee生成的文件中,直接将用户输入的用户名和密码明文的作为配置语句,即如果我是一个黑客,我可以通过你的models.py文件就知道你的数据库的密码是什么。SQLAlchemy的models.py中只有对应的表和字段信息,并没有连接数据库的配置信息,需要再后续的程序中再进行配置,使其应用更加灵活。当然 peewee只是轻量服务,给自己用足够了,不需要考虑这么多的安全问题。

2. 查数据

查数据,比如我要从Usertable中获取全部的username。sql语句为: select username from usertable。两种实现如下

# peewee版本

from models import UserTable

# 我一般习惯使用dicts的方式进行转化结果,然后通过循环获取其指定字段
result = [term['username'] for term in UserTable.select(UserTable.username).dicts()]

# SQLAlchemy版本,这里[4]使用了connection和session两种方法进行获取数据,都很强大。共同点是都需要生成一个存储了连接配置的engine来进行后面的查询操作。

from sqlalchemy import create_engine
engine = create_engine('用户名:密码@localhost:3306/数据库名')  # 同上面生成models.py命令中使用的配置   
# 创建数据库连接资源完成connect 和 session

#  使用connection的方式获取
connection = engine.connect()
sql = "select username from usertable"
result = connection.execute(sql)  # 使用语句直接获取结果

connection.close()  # 关闭connection

# 使用session方式进行获取

from models import Usertable
from sqlalchemy.orm import sessionmaker
DBSession = sessionmaker(bind=engine)
with DBSession() as session:
    # 创建Query查询,filter是where条件,最后调用one()返回唯一行,如果调用all()则返回所有行:
    user =[term.username for term in session.query(Usertable).all()]

session.close()  # 关闭session
engine.dispose()  # 关闭引擎

3. 增数据

讲真,都用上ORM了,谁还一条一条的插数据,都是一堆一堆的存。比如peewee的insert_many方法,但是如果是大量的数据,会因为连接时间过长而报错,需要对要插入的数据进行分段,peewee提供了这样的chunked方法,辅助分段插入。

但是没有查到SQLAlchemy有分段插入,是粗暴的直接add_all就可以了,这性能也太恐怖了。

#peewee [5]

# 插入单调
Usertable.insert(username='u1').execute()

# 直接插入多条
Person.insert_many([
    {'username': 'u1'},
    {'username': 'u2'},
    {'username': 'u3'}
]
).execute()

# 分段插入插入大批量数据data,已经按照上面的格式构造好,设置一次插入100行.
from peewee import chunked
for batch in chunked(data, 100):
    Usertable.insert_many(batch).execute()

#SQLAlchemy [6][7]

"""使用all 插入单条数据"""
# 创建session对象:
session = DBSession()
# 创建新User对象:
new_user = Usertable(id='2', username='Bob')
# 添加到session:
session.add(new_user)
# 提交即保存到数据库:
session.commit()
# 关闭session:
session.close()

"""使用add_all加入批量数据"""

objects = [Usertable(username="u1"), Usertable(username="u2"), Usertable(username="u3")]
session.add_all(objects)
session.commit()

4. 删

我不习惯用ORM删除数据,这里经验不多。感觉两个删除上也差不多。

#peewee[5]

Usertable.delete().where(Usertable.username=='u1').execute()

#SQLAlchemy[8]

with DBSession() as session:
    session.query(Usertable).filter(Usertable.username == "u1").delete()
    session.commit()  # 需要commit

5. 改

#peewee[5]

# 方法一
Person.update({Usertable.username: 'u3'}).where(Usertable.username=='u1').execute()
# 方法二
Person.update({'username': 'u3'}).where(Usertable.username=='u1').execute()
# 方法三
Person.update(username='u3').where(Usertable.username=='u1').execute()

#SQLAlchemy[9]

# 1.更新单条记录

query = session.query(Usertable) 
user = query.get('u1')
user.useranme='u2'  #  直接查询修改 
session.flush()

# 2.更新多条记录

query = session.query(User)
query.filter(Usertable.username=='u2').update({Usertable.age: '15'})

3 结束语

首先,从写法上看,都有ORM一贯的简约和快捷,同时使用操作对象的手法来操作数据库字段。相比较之下,peewee能满足日常使用数据库的需求,适合于个人用户,但是如果是面向重量级的开发,虽然带了操作上的繁琐,但为了其鲁棒性和性能上的追求,最好还是转移到SQLAlchemy的使用中来。

其次,从我平时应用ORM工具的场景来看,更多的是使用在数据的查和增部分,两种方工具都可以很好的解决。

最后,言而总之,成年人还做什么选择题,自然是都要会咯。


参考:

[1] ORM框架简介:https://blog.csdn.net/papima/article/details/78219000

[2] 用Django自带MySQL ORM操作外键前的正确修改方式: https://blog.csdn.net/qq_33293040/article/details/104039503

[3] peewee vs SQLAlchemy:https://stackshare.io/stackups/peewee-vs-sqlalchemy

[4] SQLAlchemy之mysql使用手册:https://blog.csdn.net/counsellor/article/details/60139569

[5] 【Python 库】轻量级 ORM 框架 peewee 用法详解之——增删改查:https://www.cnblogs.com/gl1573/p/10380793.html

[6] 使用SQLAlchemy:https://www.liaoxuefeng.com/wiki/1016959663602400/1017803857459008

[7] SQLAlchemy ORM 批量插入数据几种方法::http://www.chenxm.cc/article/555.html

[8] Python 之 sqlalchemy删除数据: https://blog.51cto.com/12965094/2362141

[9] Python SqlAlchemy使用方法:https://www.cnblogs.com/Xjng/p/4902498.html

  • 9
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用 FastAPI 中 Tortoise-ORMSQLAlchemypeewee 进行 ORM 查询的示例。 ## Tortoise-ORM ```python from fastapi import FastAPI from tortoise import fields from tortoise.contrib.fastapi import register_tortoise, HTTPNotFoundError from tortoise.models import Model from tortoise import Tortoise class User(Model): id = fields.IntField(pk=True) name = fields.CharField(50) email = fields.CharField(50) class Meta: table = "users" app = FastAPI() @app.on_event("startup") async def startup(): await Tortoise.init( db_url="sqlite://db.sqlite3", modules={"models": ["main"]} ) await Tortoise.generate_schemas() @app.on_event("shutdown") async def shutdown(): await Tortoise.close_connections() @app.get("/users") async def get_users(): users = await User.all() return users @app.get("/users/{user_id}") async def get_user(user_id: int): user = await User.get_or_none(id=user_id) if user is None: raise HTTPNotFoundError return user register_tortoise( app, db_url="sqlite://db.sqlite3", modules={"models": ["main"]}, generate_schemas=True, add_exception_handlers=True ) ``` ## SQLAlchemy ```python from fastapi import FastAPI from sqlalchemy import create_engine, Column, Integer, String from sqlalchemy.orm import sessionmaker from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, index=True) name = Column(String(50)) email = Column(String(50)) engine = create_engine("sqlite:///db.sqlite3") SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) app = FastAPI() @app.get("/users") async def get_users(): db = SessionLocal() users = db.query(User).all() db.close() return users @app.get("/users/{user_id}") async def get_user(user_id: int): db = SessionLocal() user = db.query(User).filter(User.id == user_id).first() db.close() if user is None: raise HTTPNotFoundError return user ``` ## peewee ```python from fastapi import FastAPI from peewee import SqliteDatabase, Model, CharField, IntegerField from playhouse.shortcuts import model_to_dict db = SqliteDatabase("db.sqlite3") class User(Model): id = IntegerField(primary_key=True) name = CharField() email = CharField() class Meta: database = db table_name = "users" app = FastAPI() @app.on_event("startup") def startup(): db.connect() db.create_tables([User]) @app.on_event("shutdown") def shutdown(): db.close() @app.get("/users") async def get_users(): users = [model_to_dict(user) for user in User.select()] return users @app.get("/users/{user_id}") async def get_user(user_id: int): user = User.get_or_none(User.id == user_id) if user is None: raise HTTPNotFoundError return model_to_dict(user) ``` 注意:以上示例中的代码仅用于演示 ORM 查询的基本用法,并且没有进行错误处理。在实际应用中,你应该根据需要添加适当的错误处理和安全性检查。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值