sqlalchemy案例
python
from sqlalchemy import create_engine # 创建连接的方法
from sqlalchemy import Table, Column, String, Integer, Text, MetaData # 类
from sqlalchemy.engine import Engine # 引擎类
from sqlalchemy import inspect, select, text # 反射检查
from sqlalchemy.exc import IntegrityError # 唯一键 索引等报错
"""
# 创建一次,全局共享(模拟 config 中的数据库配置)
# mysql+pymysql://用户:密码@主机:端口/数据库名
mysql是数据库方言,pymysql是数据库驱动协议
"""
engine: Engine = create_engine(
"mysql+pymysql://root:root@127.0.0.1:3306/fastapi",
pool_size=5, # 连接池大小
max_overflow=10, # 超出 pool_size 后最多再创建 10 个连接
pool_pre_ping=True, # 每次从池取连接前先 ping 一下,避免拿到断开的连接
echo=True, # 设为 True 可打印所有 SQL,方便调试
)
def create_table(engine: Engine, request_table_name: str):
"""
确保注册表存在(并发安全)。
核心知识点:
1. MetaData() ------ 表定义的容器,类似"建表清单"
2. Table() ------ 在 Python 中定义一张表,不依赖已有数据库
3. Column() ------ 定义列名 + 类型 + 约束
4. meta.create_all() ------ 将 MetaData 中所有未创建的表创建到数据库
5. inspect(engine) ------ 反射检查数据库已有表
6. autoload_with=engine ------ 从已有数据库表反射出 Table 对象
"""
"""# ---- 知识点 1&2&3: 在 Python 中定义表结构 ----"""
meta = MetaData() # 表的容器
"""
# Table()包裹表结构
"""
table1 = Table(
request_table_name, meta, # 表名 + 所属表容器 Meta
# 下面是字段定义
Column("id", String(200), primary_key=True), # 主键
Column("comment", Text, nullable=False, comment="评论"), # 文本类型 #必填,不可空
Column("user_name", String(200), nullable=True), # 非必填,可空
Column("like_count", Integer, nullable=True),
)
"""
等价于
CREATE TABLE t_book_comment (
id VARCHAR(200) NOT NULL,
comment TEXT NOT NULL COMMENT '评论',
user_name VARCHAR(200),
like_count INTEGER,
PRIMARY KEY (id)
)
"""
"""
# ---- 知识点 5: inspect 反射检查 ----
"""
inspector = inspect(engine) # inspect,英文:检查
exists = inspector.has_table(request_table_name)
"""
# inspector 可以得到表信息、字段、索引等信息
"""
print(f"表 {request_table_name} 是否存在: {exists}") # 首次创建表之前返回False
"""
# ---- 知识点 4: 自动建表 ----
# create_all 是幂等的:只会创建不存在的表,已存在的表跳过
# tables=[registry] 指定仅创建这张表,不创建 MetaData 中其他表
"""
meta.create_all(engine, tables=[table1])
"""# ---- 知识点 5: inspect 反射检查 ----"""
inspector = inspect(engine)
exists = inspector.has_table(request_table_name)
print(f"表 {request_table_name} 是否存在: {exists}") # 创建完就存在了
"""
# ---- 知识点 6: autoload_with 反射已有表 ----
# 从数据库读出已有表结构,生成 Table 对象
"""
meta_reflect = MetaData()
"""# 最新表信息"""
latest_table_info = Table(request_table_name, meta_reflect, autoload_with=engine)
"""
# 现在 registry_reflected 包含了数据库中的真实列信息
# 手动增加列后,能拿到最新的列
"""
print(
f"反射得到的列名: {[c.name for c in latest_table_info.columns]}") # 反射得到的列名: ['id', 'comment', 'user_name', 'like_count']
def queryById(engine: Engine, request_id: str, request_table_name: str):
"""
查询注册表 ------ 使用 select() 构建查询。
核心知识点:
1. select() ------ SQLAlchemy 核心的 SELECT 构建方式
2. table.c.column_name ------ 通过 .c 访问 Table 对象的列
3. engine.connect() ------ 获取连接,用上下文管理器自动归还
4. conn.execute() ------ 执行查询,返回 Result 对象
5. .first() ------ 取第一条结果
"""
"""
#meta = MetaData()
# autoload_with 从数据库反射表结构
#beQueryedTable = Table(request_table_name, meta, autoload_with=engine)
# ---- 知识点 1&2: 构建 SELECT ----
# 等价于: select comment from t_book_comment where id = 1 limit 1
"""
# 下面这个完全可以
query = text(f"select comment from t_book_comment where id = '{request_id}'")
"""
# first()等同于.fetchone(),得到结果的第一个元组, result[0] 返回第一个元组中的一个元素
# .fetchall() 能拿到所有记录,[(), (), ...]
# result = conn.execute(query).fetchall() , result[0]是('张三的评论',) 元组
"""
with engine.begin() as conn:
result = conn.execute(query).first()
"""感觉这TM才好写啊,组装的恶心死了,而且还能用"""
"""# result = conn.execute(text("select comment from t_book_comment where id = 1")).first()"""
return result[0] if result else None
"""# from datetime包 import datetime类"""
from datetime import datetime
def insertList(engine: Engine, request_table_name: str):
"""
注册新表 ------ 使用 engine.begin() 自动事务 + 处理并发冲突。
核心知识点:
1. engine.begin() ------ 自动事务,exit 时自动 commit,异常时自动 rollback
2. table.insert() ------ 构建 INSERT 语句
3. conn.execute(statement) ------ 执行 INSERT
4. IntegrityError ------ 捕获唯一键冲突(并发场景)
"""
meta = MetaData()
lastest_table_info = Table(request_table_name, meta, autoload_with=engine)
now = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
print("当前时间:" + now)
import uuid
firstDataId = str(uuid.uuid4())
dataDicList = [
{"id": firstDataId, "comment": "李四的评论", "user_name": "李四", "like_count": 5},
{"id": str(uuid.uuid4()), "comment": "王五的评论", "user_name": "王五", "like_count": 0},
{"id": str(uuid.uuid4()), "comment": "这是一条很长的评论内容......", "user_name": "赵六", "like_count": 10},
{"id": str(uuid.uuid4()), "comment": "测试评论", "user_name": None, "like_count": 1}
]
try:
with engine.begin() as conn:
"""
# 单数据插入
# {"id": "6", "comment": "测试评论", "user_name": None, "like_count": 1}
"""
insert_stmt = lastest_table_info.insert().values(
# 批量插入
dataDicList
)
"""# <class 'sqlalchemy.sql.dml.Insert'>"""
print(type(insert_stmt))
conn.execute(insert_stmt)
return firstDataId
except IntegrityError as e:
print("唯一键冲突:" + str(e))
def update(engine: Engine, request_table_name: str, request_id: str):
try:
sql = text(f"update {request_table_name} set comment = '张三的评论' where id = '{request_id}'")
with engine.begin() as conn:
conn.execute(sql)
return request_table_name
except IntegrityError as e:
"""
# ---- 知识点 4: 并发冲突处理 ----
# 另一个进程已经插入了相同的 schema_key(唯一索引冲突)
"""
"""唯一键冲突:(pymysql.err.IntegrityError) (1062, "Duplicate entry '1' for key 'PRIMARY'")"""
print("唯一键冲突:" + str(e))
def deleteById(engine: Engine, request_table_name: str, request_id: str):
try:
"""
# ---- 知识点 1: begin() 自动事务 ----
# 进入 with 块时开启事务,离开时自动 commit
# 如果抛出异常,自动 rollback
"""
sql = text(f"delete from {request_table_name} where id = '{request_id}'")
with engine.begin() as conn:
# ---- 知识点 2&3: 构建并执行 INSERT ----
conn.execute(sql)
print("通过ID删除" + request_id)
return request_table_name
except Exception as e:
print(str(e))
def dropTable(engine: Engine, request_table_name: str):
try:
"""
# ---- 知识点 1: begin() 自动事务 ----
# 进入 with 块时开启事务,离开时自动 commit
# 如果抛出异常,自动 rollback
"""
sql = text(f"DROP TABLE IF EXISTS {request_table_name}")
with engine.begin() as conn:
conn.execute(sql)
print("表:" + request_table_name + "被删除")
return request_table_name
except IntegrityError as e:
"""---- 知识点 4: 并发冲突处理"""
"""另一个进程已经插入了相同的 schema_key(唯一索引冲突)"""
print("唯一键冲突:" + str(
e)) # 唯一键冲突:(pymysql.err.IntegrityError) (1062, "Duplicate entry '1' for key 'PRIMARY'")
if __name__ == '__main__':
dropTable(engine=engine, request_table_name="t_book_comment")
"""使用sqlalchemy创建表"""
create_table(engine=engine, request_table_name="t_book_comment")
firstDataId = insertList(engine=engine, request_table_name="t_book_comment")
""" {"id": firstDataId, "comment": "李四的评论", "user_name": "李四", "like_count": 5} """
res = queryById(engine, request_id=firstDataId, request_table_name="t_book_comment")
"""李四的评论"""
print(res)
update(engine=engine, request_table_name="t_book_comment", request_id=firstDataId)
res = queryById(engine, request_id=firstDataId, request_table_name="t_book_comment")
print(res)
deleteById(engine=engine, request_table_name="t_book_comment", request_id=firstDataId)