v0.1 fastapi: route + model + orm
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
import pymysql.cursors
|
||||
|
||||
# Connect to the database
|
||||
connection = pymysql.connect(
|
||||
host="localhost",
|
||||
user="geeker",
|
||||
password="geeker",
|
||||
database="fastapi_tutor",
|
||||
cursorclass=pymysql.cursors.DictCursor,
|
||||
)
|
||||
|
||||
with connection:
|
||||
with connection.cursor() as cursor:
|
||||
# Create a new record
|
||||
sql = "INSERT INTO `users` (`email`, `password`) VALUES (%s, %s)"
|
||||
cursor.execute(sql, ("webmaster@python.org", "very-secret"))
|
||||
|
||||
# connection is not autocommit by default. So you must commit to save
|
||||
# your changes.
|
||||
connection.commit()
|
||||
|
||||
with connection.cursor() as cursor:
|
||||
# Read a single record
|
||||
sql = "SELECT `id`, `password` FROM `users` WHERE `email`=%s"
|
||||
cursor.execute(sql, ("webmaster@python.org",))
|
||||
result = cursor.fetchone()
|
||||
print(result)
|
||||
@@ -0,0 +1,4 @@
|
||||
id title content user_id created_at
|
||||
-- ---------- ------------- ------- --------------------------
|
||||
1 Alice 的第一篇 Hello world 1 2026-09-18 22:42:47.786259
|
||||
2 Alice 的第二篇 SQLAlchemy 真香 1 2026-09-18 22:42:48.010497
|
||||
@@ -0,0 +1,234 @@
|
||||
from sqlalchemy import create_engine, Column, Integer,String,DATETIME,DECIMAL, or_, and_,not_,func
|
||||
from sqlalchemy.orm import declarative_base, sessionmaker
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
db_url = "mysql+pymysql://geeker:geeker@localhost:3306/fastapi_tutor?charset=utf8mb4"
|
||||
|
||||
engine = create_engine(db_url, pool_size= 50, echo=True)
|
||||
|
||||
Base = declarative_base() # 获取基类!
|
||||
|
||||
class Order(Base): # python里的表名字
|
||||
__tablename__ = 'order_info_detail' # 实际数据库的表名字
|
||||
id = Column( Integer # 声明字段的数据类型
|
||||
, primary_key=True # 声明是主键
|
||||
, autoincrement=True # 声明是自增主键
|
||||
, comment='订单编号,自增主键' # 注释
|
||||
)
|
||||
title = Column( String(50) # 对应数据库的varchar类型
|
||||
,nullable=False # 不允许为null
|
||||
)
|
||||
price = Column(DECIMAL)
|
||||
userid = Column( Integer
|
||||
# ,ForeignKey('user_info_detail.id')
|
||||
,nullable=False ) # 外键约束
|
||||
create_date = Column(DATETIME
|
||||
, default=datetime.now # 默认值是当前时间
|
||||
)
|
||||
update_date = Column(DATETIME
|
||||
, default=datetime.now # 首次创建的时间跟更新时间一致
|
||||
, onupdate=datetime.now
|
||||
)
|
||||
|
||||
class User(Base): # python里的表名字
|
||||
__tablename__ = 'user_info_detail' # 实际数据库的表名字
|
||||
id = Column( Integer # 声明字段的数据类型
|
||||
, primary_key=True # 声明是主键
|
||||
, autoincrement=True # 声明是自增主键
|
||||
, comment='用户编号,自增主键' # 注释
|
||||
)
|
||||
username = Column( String(50) # 对应数据库的varchar类型
|
||||
,nullable=False # 不允许为null
|
||||
,comment='用户名'
|
||||
)
|
||||
create_date = Column(DATETIME
|
||||
, default=datetime.now # 默认值是当前时间
|
||||
)
|
||||
update_date = Column(DATETIME
|
||||
, default=datetime.now # 首次创建的时间跟更新时间一致
|
||||
, onupdate=datetime.now
|
||||
)
|
||||
|
||||
Base.metadata.create_all(engine) # 所有表创建 <- 只要数据库存在这个表了,就不会触发,也不会更新表结构
|
||||
# Order.__table__.create(engine,checkfirst = True) # 单张表的创建
|
||||
|
||||
|
||||
# 三、【表数据的新增】
|
||||
'''1.实例化数据库表模型类,得到具体的数据'''
|
||||
u1 = User( username='清风飞扬' )
|
||||
print( 'User1的属性:',u1.id,u1.username,u1.create_date,u1.update_date )
|
||||
u2 = User( username='北大青鸟' )
|
||||
print( 'User2的属性:',u2.username)
|
||||
|
||||
'''2.将实例化好的数据,添加到数据库里'''
|
||||
# 拿到数据库会话管理工具
|
||||
Session = sessionmaker( bind=engine
|
||||
,autoflush=False # 是否开启自动刷新python新增的数据到实际的数据库内存缓存中
|
||||
,autocommit = False # 不自动提交数据,这个是真正提交到数据库磁盘永久生效的
|
||||
)
|
||||
|
||||
session = Session() # 实例化一个数据库操作会话
|
||||
# session.add( u1 ) # 新增数据到数据库里
|
||||
# session.add( u2 )
|
||||
session.commit() # 提交,永久生效,注意提交后不能回滚
|
||||
|
||||
print(session.query(User).all())
|
||||
#
|
||||
# # session.rollback() # 回滚,事务没有提交前,dml操作可以回滚
|
||||
# session.close() # 关闭数据操作会话
|
||||
#
|
||||
#
|
||||
# '''
|
||||
# 等价于sql语句:
|
||||
# insert into user_info_detail(username) values('清风飞扬');
|
||||
# insert into user_info_detail(username) values('北大青鸟');
|
||||
# '''
|
||||
#
|
||||
# # 四、【表数据的更改】
|
||||
# session = Session() # 实例化一个数据库操作会话
|
||||
# print(f'{session.query(User)=}, {session.query(User).all()=}')
|
||||
# session.query(User)\
|
||||
# .filter( User.username == '北大青鸟' )\
|
||||
# .update( {"username":'沃林数智'
|
||||
# , "create_date":"2000-10-10"
|
||||
# } # 同时更新多个字段
|
||||
# )
|
||||
# session.commit()
|
||||
# '''
|
||||
# 多个复合条件:
|
||||
# filter( 条件1,条件2,条件3.. ) # and的关系
|
||||
#
|
||||
# filter( or_( 条件1,条件2), not_(条件2),and_(条件1,条件2) )
|
||||
#
|
||||
# session.query(表模型).filter(条件1).filter(条件2).filter(条件3) 条件可以拼接成and的关系
|
||||
#
|
||||
# or_: 函数里的条件都是 or 的关系
|
||||
# and_: 函数里的条件是 and 的关系
|
||||
# not_ : 将条件的结果进行取反
|
||||
# '''
|
||||
#
|
||||
# # 五、【表数据的删除】
|
||||
# try:
|
||||
# session.query( User ).filter( User.username == '清风飞扬' ).delete() # 只要命中就删除
|
||||
# session.commit()
|
||||
# except:
|
||||
# session.rollback() # 如果报错,就回滚
|
||||
#
|
||||
# # 清空数据,并且重置自增主键 truncate table 表名; 无法回滚
|
||||
# # session.execute( text( "truncate table user_info_detail" ) ) # 直接书写原生的sql语句
|
||||
# # 可以select、update、delete全部支持
|
||||
|
||||
|
||||
|
||||
'''1.单表基础查询'''
|
||||
# ① 单条数据查询 first(),返回的是一个查询结果对象
|
||||
r1 = session.query(User).first()
|
||||
print('获取单个查询结果对象:',r1)
|
||||
print(r1.id,r1.usernmae,r1.create_date,r1.update_date)
|
||||
|
||||
# ② 全量查询 all() , 返回的是一个列表list
|
||||
r2 = session.query(User).all()
|
||||
print('获取多个查询结果对象:',r2)
|
||||
for i in range( len(r2) ):
|
||||
print(f'第{i+1}行数据:',r2[i].id,r2[i].usernmae,r2[i].create_date,r2[i].update_date)
|
||||
|
||||
q = session.query(User) # 得到一个查询语句对象
|
||||
r3 = q.all() # 获取查询语句对象的所有内容
|
||||
print(q)
|
||||
print(r3)
|
||||
|
||||
# ③ where 条件查询
|
||||
r4 = session.query(User).filter( or_(User.id == 1, User.usernmae == '李四') ).all()
|
||||
for i in r4:
|
||||
print(i.id,i.usernmae)
|
||||
|
||||
# ④ like模糊匹配查询
|
||||
r5 = session.query(User).where( User.usernmae.like('%三%') ).all()
|
||||
for i in r5:
|
||||
print('like模糊匹配:',i.id, i.usernmae)
|
||||
|
||||
# ⑤ order by排序
|
||||
r5 = session.query(User).order_by(User.id.desc(), User.usernmae).all()
|
||||
for i in r5:
|
||||
print('排序:', i.id, i.usernmae)
|
||||
|
||||
# ⑥ limit 分页
|
||||
'''
|
||||
分页公式:
|
||||
原生sql:limit( (页数-1)*每页数据量, 每页数据量 )
|
||||
orm里: offset( (页数-1)*每页数据量 ).limit(每页数据量)
|
||||
'''
|
||||
n = 1 # 页数
|
||||
m = 2 # 每页数据量
|
||||
|
||||
r6 = session.query(User).offset((n-1)*m).limit(m).all()
|
||||
for i in r6:
|
||||
print('分页:', i.id, i.usernmae)
|
||||
|
||||
'''2.分组聚合group by'''
|
||||
'''
|
||||
select usernmae,count(1)
|
||||
from user_info_detail
|
||||
group by usernmae
|
||||
having count(1) >= 2
|
||||
'''
|
||||
r7 = session.query(User.usernmae,func.count(User.id).label('num')).\
|
||||
group_by( User.usernmae ).\
|
||||
having( func.count(User.id) >= 2 ).\
|
||||
all()
|
||||
|
||||
for i in r7:
|
||||
print('分组聚合:',i.usernmae,i.num)
|
||||
|
||||
'''
|
||||
func函数支持:max、min、sum、count、avg常用的聚合函数
|
||||
用法: func.count(聚合的字段)
|
||||
'''
|
||||
|
||||
'''3.多表关联查询join内连接/outerjoin左连接'''
|
||||
r8 = session.query(User.id.label('user_id') , User.usernmae, Order.id.label('order_id'), Order.title ).\
|
||||
join( Order, User.id == Order.userid ).\
|
||||
order_by(User.id).\
|
||||
all()
|
||||
|
||||
for i in r8:
|
||||
print('多表关联内连接:',i.user_id,i.usernmae,i.order_id,i.title)
|
||||
|
||||
# outerjoin(谁放在里面,谁就是从表)
|
||||
r8 = session.query(User.id.label('user_id') , User.usernmae, Order.id.label('order_id'), Order.title ).\
|
||||
outerjoin( Order, User.id == Order.userid ).\
|
||||
order_by(User.id).\
|
||||
all()
|
||||
|
||||
for i in r8:
|
||||
print('多表关联左连接:',i.user_id,i.usernmae,i.order_id,i.title)
|
||||
|
||||
'''
|
||||
select ——> session.query( 表模型名 )
|
||||
from 表1
|
||||
join 表2 on 条件 ——> join(连接的表,条件) / outerjoin(连接的表,条件)
|
||||
where 筛选条件 ——> filter(条件)
|
||||
group by 分组字段 ——> group_by(分组的字段)
|
||||
having 分组后过滤 ——> having(过滤的条件)
|
||||
order by 排序 ——> order_by( 排序的字段.desc() )
|
||||
limit 分页 ——> offset(偏移量).limit(数据量)
|
||||
'''
|
||||
|
||||
n = 1 # 页数
|
||||
m = 2 # 每页数据量
|
||||
r9 = session.query(User.usernmae,Order.title,func.count(1).label('ordernum')).\
|
||||
join(Order,User.id == Order.userid).\
|
||||
filter(Order.title == '苹果').\
|
||||
group_by(User.usernmae,Order.title).\
|
||||
having(func.count(1) >= 1).\
|
||||
order_by(func.count(1).desc()).\
|
||||
offset((n-1)*m).limit(m).\
|
||||
all()
|
||||
|
||||
for i in r9:
|
||||
print('完整的查询:',i.usernmae,i.title,i.ordernum)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
from sqlalchemy import (
|
||||
create_engine, String, Integer, ForeignKey, DateTime, select, func
|
||||
)
|
||||
from sqlalchemy.orm import (
|
||||
DeclarativeBase, Mapped, mapped_column, relationship,
|
||||
Session, selectinload
|
||||
)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 1. 定义 Base 和模型
|
||||
# ============================================================
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
"""所有模型的基类,SQLAlchemy 2.0 推荐写法"""
|
||||
pass
|
||||
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = "users"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
name: Mapped[str] = mapped_column(String(50), nullable=False)
|
||||
email: Mapped[str] = mapped_column(String(100), unique=True, nullable=False)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.now)
|
||||
|
||||
# 一对多:一个用户有多个文章
|
||||
posts: Mapped[List["Post"]] = relationship(
|
||||
back_populates="author",
|
||||
cascade="all, delete-orphan", # 删除用户时级联删除文章
|
||||
lazy="selectin" # 查询用户时自动预加载文章
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<User(id={self.id}, name='{self.name}', email='{self.email}')>"
|
||||
|
||||
|
||||
class Post(Base):
|
||||
__tablename__ = "posts"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
title: Mapped[str] = mapped_column(String(200), nullable=False)
|
||||
content: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), nullable=False)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.now)
|
||||
|
||||
# 多对一:多篇文章属于一个用户
|
||||
author: Mapped["User"] = relationship(back_populates="posts")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Post(id={self.id}, title='{self.title}', user_id={self.user_id})>"
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 2. 创建引擎和表
|
||||
# ============================================================
|
||||
|
||||
engine = create_engine("sqlite:///users_posts.db", echo=False)
|
||||
Base.metadata.create_all(engine)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 3. CRUD 操作
|
||||
# ============================================================
|
||||
|
||||
def create_user(session: Session, name: str, email: str) -> User:
|
||||
user = User(name=name, email=email)
|
||||
session.add(user)
|
||||
session.commit()
|
||||
session.refresh(user) # 刷新以获取数据库生成的 id
|
||||
return user
|
||||
|
||||
|
||||
def create_post(session: Session, title: str, content: str, user_id: int) -> Post:
|
||||
post = Post(title=title, content=content, user_id=user_id)
|
||||
session.add(post)
|
||||
session.commit()
|
||||
session.refresh(post)
|
||||
return post
|
||||
|
||||
|
||||
def get_user_by_id(session: Session, user_id: int) -> Optional[User]:
|
||||
return session.get(User, user_id)
|
||||
|
||||
|
||||
def get_user_by_email(session: Session, email: str) -> Optional[User]:
|
||||
stmt = select(User).where(User.email == email)
|
||||
return session.execute(stmt).scalar_one_or_none()
|
||||
|
||||
|
||||
def get_all_users(session: Session) -> List[User]:
|
||||
stmt = select(User).order_by(User.id)
|
||||
return list(session.execute(stmt).scalars().all())
|
||||
|
||||
|
||||
def update_user_email(session: Session, user_id: int, new_email: str) -> bool:
|
||||
user = session.get(User, user_id)
|
||||
if not user:
|
||||
return False
|
||||
user.email = new_email
|
||||
session.commit()
|
||||
return True
|
||||
|
||||
|
||||
def delete_user(session: Session, user_id: int) -> bool:
|
||||
user = session.get(User, user_id)
|
||||
if not user:
|
||||
return False
|
||||
session.delete(user) # cascade 会自动删除关联的 posts
|
||||
session.commit()
|
||||
return True
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 4. 复杂查询
|
||||
# ============================================================
|
||||
|
||||
def get_users_with_post_count(session: Session):
|
||||
"""统计每个用户的文章数"""
|
||||
stmt = (
|
||||
select(User.name, func.count(Post.id).label("post_count"))
|
||||
.outerjoin(Post, User.id == Post.user_id)
|
||||
.group_by(User.id)
|
||||
.order_by(func.count(Post.id).desc())
|
||||
)
|
||||
return session.execute(stmt).all()
|
||||
|
||||
|
||||
def get_posts_by_user(session: Session, user_id: int) -> List[Post]:
|
||||
"""查询某个用户的所有文章"""
|
||||
stmt = select(Post).where(Post.user_id == user_id).order_by(Post.created_at.desc())
|
||||
return list(session.execute(stmt).scalars().all())
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 5. 演示
|
||||
# ============================================================
|
||||
|
||||
def main():
|
||||
with Session(engine) as session:
|
||||
# 清理旧数据(演示用)
|
||||
session.query(Post).delete()
|
||||
session.query(User).delete()
|
||||
session.commit()
|
||||
|
||||
# 创建用户
|
||||
alice = create_user(session, "Alice", "alice@example.com")
|
||||
bob = create_user(session, "Bob", "bob@example.com")
|
||||
print("创建用户:", alice, bob)
|
||||
|
||||
# 创建文章
|
||||
create_post(session, "Alice 的第一篇", "Hello world", alice.id)
|
||||
create_post(session, "Alice 的第二篇", "SQLAlchemy 真香", alice.id)
|
||||
create_post(session, "Bob 的独苗", "Python ORM", bob.id)
|
||||
|
||||
# 查询
|
||||
print("\n所有用户:")
|
||||
for u in get_all_users(session):
|
||||
print(f" {u} -> 文章: {[p.title for p in u.posts]}")
|
||||
|
||||
print("\n用户文章数统计:")
|
||||
for name, count in get_users_with_post_count(session):
|
||||
print(f" {name}: {count} 篇")
|
||||
|
||||
# 更新
|
||||
update_user_email(session, alice.id, "alice_new@example.com")
|
||||
print("\n更新后:", get_user_by_id(session, alice.id))
|
||||
|
||||
# 删除(级联删除文章)
|
||||
delete_user(session, bob.id)
|
||||
print("\n删除 Bob 后,所有用户:")
|
||||
for u in get_all_users(session):
|
||||
print(f" {u}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Binary file not shown.
Reference in New Issue
Block a user