赞
踩
作为从隔壁Java村来的Python小白,接触Python后,就像乡下人进城了一样——新奇!今年年初接了一个用Python Scrapy + FastAPI 重构原本 Java 写的爬虫网页分析逻辑。那到管道数据持久层的一步时,不能直接存储,而是要先查询,比对数据,再进行存储,而一些简单的Python连接数据库操作我始终觉得太像JDBC,每次都要这样连,似乎会重复工作量。如果能有像Mybatis一样的ORM框架就好了。
果然,搜了一圈发现了一位宝藏up主跟峰哥学编程的教程:SQLAlchemy ORM框架的使用
学习目标
准备工作
准备一个python项目,这里我准备的是FastAPI项目,在requirements.txt加入各依赖:
mysqlclient==2.1.1
SQLAlchemy==2.0.23
或者 pip install 也可以。
准备好环境后,下面我们开始!
SQLAlchemy+mysqldb 连接数据库,示例代码如下:
# 所需依赖 import sqlalchemy # 创建引擎 engine = sqlalchemy.create_engine('<数据库类型>://<用户名>:<密码>@<地址>:<端口号>/<数据库名>?[编码方式]') # 创建连接 connect = engine.connect() # 示例查询 query = sqlalchemy.text('SELECT id, user_name, create_time, update_time FROM kcl_user') # 返回的是值的集合 result_set = connect.execute(query) for row in result_set: print(row) # 关闭连接 connect.close()
其中,具体创建引擎应该参考上述代码改为自己实际的,我这里的示例做了描述,< >
代表必填,[ ]
代表可选项,例如连接本地的是:
engine = sqlalchemy.create_engine('mysql://root:123456@<地址>:<端口号>/<数据库名>?[编码方式]')
创建表的需求在一些爬虫需求里比较常见。以下演示如何创建表:
# 暂存元数据
meta_data = sqlalchemy.MetaData()
# 创建表
subject = sqlalchemy.Table(
'subject',meta_data,
sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True),
sqlalchemy.Column('name', sqlalchemy.String(255), unique=True, nullable=False),
sqlalchemy.Column('score', sqlalchemy.Integer, unique=False),
)
meta_data.create_all(engine)
其中,nullable 标识是否为空,false表示不可以,unique表示是否唯一,ture表示是。
而且,我们在实际使用的时候可以大胆执行meta_data.create_all(engine)方法,若该表存在,则不会覆盖掉它,哪怕字段变更了。
这一小节我们来学习SQLAlchemy的一些CRUD基本操作。用我们上一小节刚刚创建的表做示例
若读者有Python或Java等编程语言基础+SQL基础,那么基础的CRUD就很好理解了,我们只需要直接通过示例代码了解SQLAlchemy提供了何种方法,如何传参,如何提交即可:
新增单个
# 创建引擎 engine = sqlalchemy.create_engine('mysql://root:root@localhost:3306/kcl2024?charset=utf8', echo=True) # 暂存元数据 meta_data = sqlalchemy.MetaData() # 创建表 subject = sqlalchemy.Table( 'subject',meta_data, sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True), sqlalchemy.Column('name', sqlalchemy.String(255), unique=True, nullable=True), sqlalchemy.Column('score', sqlalchemy.Integer, unique=False), sqlalchemy.Column('create_time', sqlalchemy.DateTime, unique=False), ) meta_data.create_all(engine) # 新增单个 insert_single = subject.insert().values(name='数学', score=100, create_time='2021-01-01 00:00:00') with engine.connect() as connection: result = connection.execute(insert_single) print(result.inserted_primary_key) # 事务提交 connection.commit()
批量新增
批量新增需要在execute带上.insert(),和一个字典列表:
# 新增多个
dict_list = [
{'name': '体育', 'score': 100},
{'name': '英语', 'score': 100},
{'name': '物理', 'score': 100},
{'name': '化学', 'score': 100},
{'name': '生物', 'score': 100},
{'name': '政治', 'score': 100},
]
with engine.connect() as connection:
result_multi = connection.execute(subject.insert(), dict_list)
# 事务提交
connection.commit()
示例:
with engine.connect() as conn:
# 查询数据
query = subject.select().where(subject.c.name == '语文')
result = conn.execute(query)
print(f'result_set={result.fetchall()}')
# 关闭连接
conn.close()
其中, 上面的.c
表示column
其它条件
where里如果我们要加or、and等条件,需要额外引入sqlalchmy的对应方法,例如:
with engine.connect() as conn:
# 查询数据
query = subject.select().where(
or_(subject.c.name == '数学',subject.c.name == '语文'),
and_(subject.c.score > 60, subject.c.score <= 100)
)
result = conn.execute(query)
print(f'result_set={result.fetchall()}')
# 关闭连接
conn.close()
有了上面的基础,我们学习删除和修改将会快很多,直接上示例:
# 删除数据
conn.execute(subject.delete().where(subject.c.name == '语文'))
query = subject.select()
result = conn.execute(query)
print(f'result_set={result.fetchall()}')
示例:
# 更新数据
conn.execute(subject.update().where(subject.c.id == 1).values(name='书法', score=99))
query = subject.select().where(
id == 1,
)
result = conn.execute(query)
print(f'result_set={result.fetchall()}')
学到这里,我们发现SQLAlchemy的用法和Mybatis-Plus很像!
SQLAlchemy 也是一个 ORM对象关系映射框架,和Java的Mybatis、Mybatis-plus 类似,也支持直接通过对象自动映射到表,从而更方便CRUD的操作。
Mybatis 需要有对应的实体类,SQLAlchemy 也一样。实体类的属性其实对应的就是表的字段,因此这里的实体类形式很像对表的描述,例如:
from sqlalchemy import create_engine, Column, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
# 创建引擎
engine = create_engine('mysql://root:root@localhost:3306/kcl2024?charset=utf8', echo=True)
Base = declarative_base()
# 定义映射关系类
class Subject(Base):
__tablename__ = 'subject'
id = Column(Integer, primary_key=True)
name = Column(String(255), unique=True, nullable=True)
score = Column(Integer, unique=False)
这里介绍通过ORM的方式实现CRUD
篇幅有限,我们假定我们已经前提准备好Session
事实上语法与我们直接从connection执行sql很像,区别仅在于Session与对象实例传参方式,直接上示例:
# 创建实例
math = Subject(name='math', score=100)
chinese = Subject(name='chinese', score=90)
# 添加实例到 session
subject_list = [math, chinese]
# 添加实例到 session
session.add_all(subject_list)
session.commit()
这个示例演示了如何批量新增。单个新增参考connection的方式添加单个对象即可。最后别忘了commit哦。
这里介绍几种常见的查询情况:
所有记录
# 查询所有记录
result = session.query(Subject).all()
# 遍历获取记录列表
for subject in result:
print(subject.name, subject.score,type(subject))
查询出来的就是我们的Subject对象类型列表。
一条记录的情况
# 查询第一条记录
result_first = session.query(Subject.id, Subject.name, Subject.score).first()
if result_first:
print(result_first.name, result_first.score)
# 只有一条记录的情况
result_one = session.query(Subject).filter(Subject.id == 1).one()
print(result_one.name, result_one.score)
# scalar() 返回第一列的值
result_scalar = session.query(Subject.name).filter(Subject.id == 1).scalar()
print(result_scalar)
查询记录数量
# 查询记录数量
result_count = session.query(Subject).count()
print(result_count)
删除很简单,只需要调用delete()方法,我们直接看例子:
# 删除
session.query(Subject).filter(Subject.id == 22).delete()
result = session.query(Subject).all()
for subject in result:
print(subject.id, subject.name, subject.score)
SQLAlchemy 提供了如下修改方式,我们直接看示例:
# 修改方式1 ,先查询后修改 subject1 = session.query(Subject).filter(Subject.id == 1).first() subject1.name = 'Java' session.commit() # 修改方式2 ,直接修改 session.query(Subject).filter(Subject.id == 2).update({'name': '高数'}) # 批量直接修改 session.query(Subject).filter(Subject.id > 2, Subject.id < 5).update({'score': 99}) # 查询全部 result = session.query(Subject).all() for subject in result: print(subject.id, subject.name, subject.score)
值得注意的是,随便定一个对象是不能直接commit的,commit中间必须是从表里查询出来的对象。
为了代码更复用,我们可以直接使用sqlalchemy.orm的Session:
from sqlalchemy import delete
from sqlalchemy.orm import Session
from db_init import engine
from orm_init import Subject
def batch_deleted():
with Session(engine) as session:
# session.excute 当 score < 100 时删除
session.execute(delete(Subject).where(Subject.score < 100))
result = session.query(Subject).all()
for subject in result:
print(subject.id, subject.name, subject.score)
使用 表的 join 方法。
对于现阶段的我们来说,由于Alchemy也只是作为一个python写的工具或者中间件以及独立的项目,一般还用不上join。真要join的情况一般还是在Java里的Mybatis/Mybtais-plus实现。
篇幅有限,这里暂时不描述了。
详情请参考参考教程以及官方文档:sqlalchemy官方文档
Mapped映射方式较新版本的SQLAlchemy特性。有了上文的基础,我们举个例子,就很好理解了。效果和colunm的映射方式是一致的:
from sqlalchemy.orm import sessionmaker, Mapped, mapped_column
# Mappped 的方式定义映射关系类
class Student(Base):
__tablename__ = 'student'
id: Mapped[int] = mapped_column(Column(Integer, primary_key=True))
name: Mapped[str] = mapped_column(Column(String(255), unique=True, nullable=True))
score: Mapped[int] = mapped_column(Column(Integer, unique=False))
subject: Mapped[str] = mapped_column(Column(String(255), unique=False))
Base.metadata.create_all(engine)
我们在Java字段中,常用BaseEntity来存放公共字段,然后用具体的entity extends 这个BaseEntity。在Python的SQLAlchemy中,类似的操作是用Annotated抽取公共字段,如下例子:
from typing_extensions import Annotated
# 公共字段
int_primary_key = Annotated[int, mapped_column(Integer, primary_key=True)]
# Mappped 的方式定义映射关系类
class Student(Base):
__tablename__ = 'student'
id: Mapped[int_primary_key]
name: Mapped[str] = mapped_column(String(255), unique=True, nullable=True)
score: Mapped[int] = mapped_column(unique=False)
subject: Mapped[str] = mapped_column(String(255), unique=False)
本文我们学习了Python的SQLAlchemy ORM 映射框架的使用。
我们首先学习了基于Connection的SQL语句执行,然后重点学习了基于ORM的映射CRUD。实际工作与学习中,基于ORM的方式更常用。
在学习的过程中,我们发现,Python 的SQLAlchemy使用方式与Java的Mybatis-Plus比较像,除了不用写xml,内置的方法单表查询也够用了。
感谢大家阅读此文,希望我的内容能够给您带来启发和帮助。如果您喜欢我的文章,欢迎点赞、评论和分享,让更多人看到。期待与您在下一篇文章再相会!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。