(十六)当前用户、CRUD 视图与事务结果
1. 成功保存与失败回显
# 续写同一文件:reading_project/src/library/views.py
def book_form(request, book=None):
# POST 即使为空也要绑定,让 required 字段给出错误。
form = BookForm(
request.POST if request.method == "POST" else None,
instance=book,
user=request.user,
)
status = 200
if request.method == "POST":
status = 422
if form.is_valid():
try:
saved = save_book(form, request.user)
except IntegrityError as exc:
if (
getattr(exc.__cause__, "sqlite_errorname", "")
!= "SQLITE_CONSTRAINT_UNIQUE"
):
raise
# 在 atomic 外处理异常;本次事务已经回滚。
form.add_error(None, "保存时发生同名冲突,请修改后重试")
status = 409
else:
messages.success(request, "已保存阅读记录")
return redirect("library:detail", pk=saved.pk)
return render(
request, "library/book_form.html", {"form": form, "book": book}, status=status
)
@login_required
def create(request):
return book_form(request)
@login_required
def edit(request, pk):
return book_form(request, owned_book(request.user, pk))
@login_required
@require_POST
def delete(request, pk):
with transaction.atomic():
owned_book(request.user, pk).delete()
messages.success(request, "已删除书籍")
return redirect("library:index")book_form 处理新增和修改共用的表单过程。GET 不绑定,POST 绑定实际提交;字段错误交回 422。通过后进入 save_book,事务成功再暂存消息并跳详情;数据库实际写入发生唯一键冲突时,事务已回滚,表单加入整体错误并返回 409。
正常的重名通常在 clean_title 阶段发现。两个请求可能都先通过检查,再先后写入,数据库唯一约束继续处理后一份冲突,这也是表单检查与数据库约束同时保留的原因。
2. 登录检查之后还要核对书籍
owned_book 查记录后比较 owner_id,其他用户得到 403。新增的归属来自 request.user,修改时仍检查原记录归属。删除同时使用 login_required 和 require_POST,GET 删除入口得到 405。