ORM概述 ORM = Object Relational Mapping(对象关系映射)。它是一种 让你用面向对象的方式操作数据库 的技术。
一句话解释: ORM = 把 Python(或其他语言)里的对象 ↔ 数据库里的表记录 互相转换的工具。
举个最直观的例子:
不用 ORM(纯 SQL)1 2 3 cursor.execute("SELECT id, name, age FROM users WHERE id = 1") row = cursor.fetchone()user = {"id": row [0 ], "name": row [1 ], "age": row [2 ]}
使用 ORM(以 SQLAlchemy 为例)1 user = session.query(User).filter_by(id =1 ).first()
数据库里的users表,就像程序里的一个User类;每个数据行就是类的实例。
也就是说:
数据库表 → 类(Class)
数据行 → 对象(Object)
表字段 → 类属性(Attribute)
为什么要用ORM? 提升开发效率 数据库操作变成写对象操作,无需手写大量 SQL。
不想写烦人的 SQL
想快速 CRUD(增删查改)
ORM 能让操作像操作 Python 对象一样简单
代码更易维护、更优雅 ORM 的代码结构清晰:1 User(name='Ming' , age=18 )
很容易理解,不像 SQL 字符串容易出错。
避免 SQL 注入风险 ORM 内部自动做参数化处理:1 session.query(User).filter (User.name == name_input)
比拼接 SQL 安全得多。
底层数据库可随时切换 例如:
开发用 SQLite
部署换 MySQL、PostgreSQL、Oracle
ORM 只需改连接字符串,不用改所有 SQL。
提供丰富功能 ORM 通常内置:
自动建表
关系维护(外键、一对多、多对多)
自动事务管理
数据验证
查询构建器(链式调用)
怎么使用ORM? 安装SQLAlchemy Python中最常用的ORM框架是SQLAlchemy
安装方式:
1 2 3 import sqlalchemysqlalchemy.__version__
'2.0.44'
定义模型(Model) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 from sqlalchemy import Column, Integer, String, UniqueConstraintfrom sqlalchemy.orm import declarative_baseBase = declarative_base() class User (Base ): __tablename__ = "users" __table_args__ = {"comment" : "用户表,存用户信息" } id = Column(Integer, primary_key=True , autoincrement=True , comment="编号" ) name = Column(String(10 ), nullable=True , comment="姓名" ) age = Column(Integer, nullable=True , comment="年龄" )
创建数据库连接 轻量化的sqlite 1 2 3 4 5 6 from sqlalchemy import create_enginefrom sqlalchemy.orm import sessionmakerengine = create_engine("sqlite:///test.db" ) SessionLocal = sessionmaker(bind=engine) session = SessionLocal()
Mysql 需要使用到Pymysql驱动
1 2 3 4 5 6 7 8 from sqlalchemy import create_enginefrom sqlalchemy.orm import sessionmakerengine = create_engine("mysql+pymysql://root:123456@localhost:3306/testdb" , pool_pre_ping=True ) SessionLocal = sessionmaker(bind=engine) session = SessionLocal()
PostgreSQL 需要使用到psycopg驱动1 pip install psycopg[binary]
1 2 3 4 5 6 7 8 9 from sqlalchemy import create_enginefrom sqlalchemy.orm import sessionmakerengine = create_engine("postgresql+psycopg://ming:123456@localhost:5432/testdb" , pool_pre_ping=True ) SessionLocal = sessionmaker(bind=engine) session = SessionLocal()
删除表 1 2 3 4 5 from sqlalchemy import textwith engine.connect() as conn: conn.execute(text("DROP TABLE IF EXISTS users" )) conn.commit()
创建表 1 Base.metadata.create_all(engine)
插入数据 1 2 3 4 5 6 7 user_tables = { "name" : ["张三" , "李四" , "lm" , "minglog" , "wang" ], "age" : [25 , 21 , 22 , 18 , 12 ] } for i in range (len (user_tables["name" ])): session.add(User(name=user_tables["name" ][i], age=user_tables["age" ][i])) session.commit()
查询数据 1 2 3 4 5 all_res = session.query(User).all () for res in all_res: print (f"编号:{res.id } ,姓名:{res.name} ,年龄:{res.age} " )
编号:1,姓名:张三,年龄:25
编号:2,姓名:李四,年龄:21
编号:3,姓名:lm,年龄:22
编号:4,姓名:minglog,年龄:18
编号:5,姓名:wang,年龄:12
1 2 3 4 all_res = session.query(User).filter_by(name="minglog" ).all () for res in all_res: print (f"编号:{res.id } ,姓名:{res.name} ,年龄:{res.age} " )
编号:4,姓名:minglog,年龄:18
1 2 3 4 all_res = session.query(User).filter (User.age <= 18 ).all () for res in all_res: print (f"编号:{res.id } ,姓名:{res.name} ,年龄:{res.age} " )
编号:4,姓名:minglog,年龄:18
编号:5,姓名:wang,年龄:12
1 2 3 4 all_res = session.query(User).filter ((User.age <= 18 ) & (User.name == "minglog" )).all () for res in all_res: print (f"编号:{res.id } ,姓名:{res.name} ,年龄:{res.age} " )
编号:4,姓名:minglog,年龄:18
1 2 3 4 all_res = session.query(User).filter ((User.age <= 18 ) | (User.name == "张三" )).all () for res in all_res: print (f"编号:{res.id } ,姓名:{res.name} ,年龄:{res.age} " )
编号:1,姓名:张三,年龄:25
编号:4,姓名:minglog,年龄:18
编号:5,姓名:wang,年龄:12
{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState at 0x76974c7c37d0>,
'name': '张三',
'age': 25,
'id': 1}
更新数据 1 2 3 4 5 6 tmp_user = session.query(User).filter_by(name="minglog" ).first() tmp_user.name = "luoming" session.commit()
1 2 3 all_res = session.query(User).all () for res in all_res: print (f"编号:{res.id } ,姓名:{res.name} ,年龄:{res.age} " )
编号:1,姓名:张三,年龄:25
编号:2,姓名:李四,年龄:21
编号:3,姓名:lm,年龄:22
编号:5,姓名:wang,年龄:12
编号:4,姓名:luoming,年龄:18
删除数据 1 2 3 4 5 6 session.delete(all_res[0 ]) session.commit() all_res = session.query(User).all () for res in all_res: print (f"编号:{res.id } ,姓名:{res.name} ,年龄:{res.age} " )
编号:2,姓名:李四,年龄:21
编号:3,姓名:lm,年龄:22
编号:5,姓名:wang,年龄:12
编号:4,姓名:luoming,年龄:18
在实际的开发使用中往往会和pydantic构成的Model结合使用 FastAPI通过pydantic可以严格的规定,接口的请求和响应的参数格式与字段类型
定义请求与响应字段格式 1 2 3 4 5 6 7 from pydantic import BaseModel, Fieldclass UserRequest (BaseModel ): name: str = Field(None , description="姓名" ) class UserResponse (BaseModel ): age: int = Field(None , description="年龄" )
定义路由 1 2 3 4 5 6 7 8 9 10 11 12 from fastapi import FastAPI, APIRouterapp = FastAPI() router = APIRouter(prefix="/user" , tags=["Users" ]) @router.get("/get_age" , response_model=UserResponse, description="用户年龄+1" ) async def age_add (request: UserRequest ): user = session.query(User).filter_by(name=request.name).first() if user is None : return {"age" : 0 } return {"age" : user.age}
路由注册 1 2 app.include_router(router)
异步开启接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import uvicornimport asyncioimport nest_asyncionest_asyncio.apply() config = uvicorn.Config( app, host="0.0.0.0" , port=8000 , reload=False ) server = uvicorn.Server(config=config) try : loop = asyncio.get_running_loop() except RuntimeError: loop = asyncio.get_event_loop() loop.run_until_complete(server.serve())
INFO: Started server process [31380]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO: Shutting down
INFO: Waiting for application shutdown.
INFO: Application shutdown complete.
INFO: Finished server process [31380]
1 2 3 4 5 6 7 8 import nest_asyncionest_asyncio.apply() import uvicornuvicorn.run(app, host="0.0.0.0" , port=8000 )