(十五)完成新增、修改与删除
1. 先把变化写进同一事务
查询已经能得到当前用户的记录,现在把模型中的变化应用到对象。下面接在 service.py 后面:
# 续写同一文件:api_project/src/bookapi/service.py
def create_book(session: Session, owner_id: int, data: BookCreate) -> BookOut:
with commit_changes(session):
book = Book(owner_id=owner_id, **data.model_dump())
session.add(book)
session.flush() # 发出 INSERT,取回 id,此时事务还没提交。
result = BookOut.model_validate(book)
return result
def patch_book(
session: Session, owner_id: int, book_id: int, data: BookPatch
) -> BookOut:
book = owned_book(session, book_id, owner_id)
with commit_changes(session):
# 只取本次实际提交的字段,False 和 [] 都会被保留。
for name, value in data.model_dump(exclude_unset=True).items():
setattr(book, name, value)
session.flush()
result = BookOut.model_validate(book)
return result
def delete_book(session: Session, owner_id: int, book_id: int):
book = owned_book(session, book_id, owner_id)
with commit_changes(session):
session.delete(book)新增时,data.model_dump() 给出书籍字段,** 把这些键值拆成构造函数参数;归属单独由服务端传入。flush() 后数据库编号可用,先用 BookOut 构造返回结果,再离开 commit_changes 提交。提交失败时抛出异常,函数不会返回成功结果。
部分修改先取到有权限的书,再遍历 exclude_unset=True 得到的变化。setattr(book, name, value) 相当于按字段名执行属性赋值。输入模型已经拒绝额外字段,所以这里处理的是 title、finished、tags 三个已知名称。False 会真正写入,空列表会真正清空标签,没出现的字段保留。
删除也先走归属检查,session.delete(book) 标记删除,提交时执行对应的 SQL。每个写入动作都有自己的事务;同一事务中的字段变化一起成功或回滚。
2. 把写入动作接到 HTTP
继续写入 routes/books.py:
# 续写同一文件:api_project/src/bookapi/routes/books.py
# 新增响应给出 Location,局部修改交给服务层,删除成功后返回不带正文的 204。
@router.post("", response_model=BookOut, status_code=201)
def add_book(
data: BookCreate, session: DbSession, identity: CurrentIdentity, response: Response
):
result = service.create_book(session, identity.user.id, data)
response.headers["Location"] = f"/books/{result.id}"
return result
@router.patch("/{book_id}", response_model=BookOut)
def update_book(
book_id: BookId, data: BookPatch, session: DbSession, identity: CurrentIdentity
):
return service.patch_book(session, identity.user.id, book_id, data)
@router.delete("/{book_id}", status_code=204)
def remove_book(book_id: BookId, session: DbSession, identity: CurrentIdentity):
service.delete_book(session, identity.user.id, book_id)
return Response(status_code=204)POST /books 新建资源,成功返回 201,Location 指向新书路径。PATCH /books/{book_id} 只应用提交的变化,返回修改后的书。DELETE 成功交回 204,没有 JSON 正文,代码显式返回空 Response。
这里的 response: Response 由 FastAPI 提供,用来设置响应头;函数返回值仍交给响应模型序列化。Location 采用相对路径,调用者可以直接用同一 API 客户端继续读取它。