删除作者表单
本小节介绍如何定义一个页面来删除Author
对象。
如“表单设计”部分所述,我们的策略是只允许删除未被其他对象引用的对象(在本例中,这意味着如果Author
被Book
引用,则不允许删除)。在实现方面,这意味着表单需要在删除作者之前确认没有关联的书籍。如果有关联的书籍,则应显示这些书籍,并说明必须先删除这些书籍,然后才能删除Author
对象。
控制器 - 获取路由
打开/controllers/authorController.js。找到导出的author_delete_get()
控制器方法,并将其替换为以下代码。
// Display Author delete form on GET.
exports.author_delete_get = asyncHandler(async (req, res, next) => {
// Get details of author and all their books (in parallel)
const [author, allBooksByAuthor] = await Promise.all([
Author.findById(req.params.id).exec(),
Book.find({ author: req.params.id }, "title summary").exec(),
]);
if (author === null) {
// No results.
res.redirect("/catalog/authors");
}
res.render("author_delete", {
title: "Delete Author",
author: author,
author_books: allBooksByAuthor,
});
});
控制器从 URL 参数(req.params.id
)获取要删除的Author
实例的 ID。它在Promise.all()
返回的 Promise 上使用await
来异步等待指定的作者记录和所有关联的书籍(并行)。当这两个操作都完成后,它会渲染author_delete.pug视图,并传递title
、author
和author_books
的变量。
注意:如果findById()
没有返回任何结果,则表示作者不在数据库中。在这种情况下,无需删除任何内容,因此我们立即重定向到所有作者的列表。
if (author === null) {
// No results.
res.redirect("/catalog/authors");
}
控制器 - 发布路由
找到导出的author_delete_post()
控制器方法,并将其替换为以下代码。
// Handle Author delete on POST.
exports.author_delete_post = asyncHandler(async (req, res, next) => {
// Get details of author and all their books (in parallel)
const [author, allBooksByAuthor] = await Promise.all([
Author.findById(req.params.id).exec(),
Book.find({ author: req.params.id }, "title summary").exec(),
]);
if (allBooksByAuthor.length > 0) {
// Author has books. Render in same way as for GET route.
res.render("author_delete", {
title: "Delete Author",
author: author,
author_books: allBooksByAuthor,
});
return;
} else {
// Author has no books. Delete object and redirect to the list of authors.
await Author.findByIdAndDelete(req.body.authorid);
res.redirect("/catalog/authors");
}
});
首先,我们验证是否已提供 ID(这是通过表单主体参数发送的,而不是使用 URL 中的版本)。然后,我们以与GET
路由相同的方式获取作者及其关联的书籍。如果没有书籍,则删除作者对象并重定向到所有作者的列表。如果仍然存在书籍,则我们只需重新渲染表单,并传入要删除的作者和书籍列表。
注意:我们可以检查对findById()
的调用是否返回任何结果,如果未返回,则立即渲染所有作者的列表。为了简洁起见,我们保留了上面的代码(如果找不到 ID,它仍然会返回作者列表,但这将在findByIdAndDelete()
之后发生)。
视图
创建/views/author_delete.pug并将下面的文本复制到其中。
extends layout
block content
h1 #{title}: #{author.name}
p= author.lifespan
if author_books.length
p #[strong Delete the following books before attempting to delete this author.]
div(style='margin-left:20px;margin-top:20px')
h4 Books
dl
each book in author_books
dt
a(href=book.url) #{book.title}
dd #{book.summary}
else
p Do you really want to delete this Author?
form(method='POST')
div.form-group
input#authorid.form-control(type='hidden', name='authorid', value=author._id )
button.btn.btn-primary(type='submit') Delete
该视图扩展了布局模板,并覆盖了名为content
的块。在顶部,它显示作者详细信息。然后,它根据author_books
的数量包含一个条件语句(if
和else
子句)。
- 如果作者有相关书籍,则页面会列出这些书籍,并说明必须先删除这些书籍,然后才能删除此
Author
。 - 如果没有书籍,则页面会显示一个确认提示。
- 如果点击删除按钮,则作者 ID 将在
POST
请求中发送到服务器,并且该作者的记录将被删除。
添加删除控件
接下来,我们将向作者详细信息视图添加删除控件(详细信息页面是从中删除记录的理想位置)。
注意:在完整的实现中,控件将仅对授权用户可见。但是,目前我们还没有授权系统!
打开author_detail.pug视图,并在底部添加以下行。
hr
p
a(href=author.url+'/delete') Delete author
现在,该控件应该显示为一个链接,如下所示,位于作者详细信息页面上。
它是什么样子的?
运行应用程序并在浏览器中打开https://127.0.0.1:3000/
。然后选择所有作者链接,然后选择特定作者。最后,选择删除作者链接。
如果作者没有书籍,您将看到如下页面。按下删除后,服务器将删除作者并重定向到作者列表。
如果作者有书籍,您将看到如下视图。然后,您可以从其详细信息页面删除书籍(一旦实现该代码!)。
注意:其他删除对象页面可以以相同的方式实现。我们将其留作挑战。
后续步骤
- 返回Express 教程第 6 部分:使用表单。
- 继续第 6 部分的最后一小节:更新书籍表单。