删除作者表单

本小节介绍如何定义一个页面来删除Author对象。

如“表单设计”部分所述,我们的策略是只允许删除未被其他对象引用的对象(在本例中,这意味着如果AuthorBook引用,则不允许删除)。在实现方面,这意味着表单需要在删除作者之前确认没有关联的书籍。如果有关联的书籍,则应显示这些书籍,并说明必须先删除这些书籍,然后才能删除Author对象。

控制器 - 获取路由

打开/controllers/authorController.js。找到导出的author_delete_get()控制器方法,并将其替换为以下代码。

js
// 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视图,并传递titleauthorauthor_books的变量。

注意:如果findById()没有返回任何结果,则表示作者不在数据库中。在这种情况下,无需删除任何内容,因此我们立即重定向到所有作者的列表。

js
if (author === null) {
  // No results.
  res.redirect("/catalog/authors");
}

控制器 - 发布路由

找到导出的author_delete_post()控制器方法,并将其替换为以下代码。

js
// 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并将下面的文本复制到其中。

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的数量包含一个条件语句(ifelse子句)。

  • 如果作者有相关书籍,则页面会列出这些书籍,并说明必须先删除这些书籍,然后才能删除此Author
  • 如果没有书籍,则页面会显示一个确认提示。
  • 如果点击删除按钮,则作者 ID 将在POST请求中发送到服务器,并且该作者的记录将被删除。

添加删除控件

接下来,我们将向作者详细信息视图添加删除控件(详细信息页面是从中删除记录的理想位置)。

注意:在完整的实现中,控件将仅对授权用户可见。但是,目前我们还没有授权系统!

打开author_detail.pug视图,并在底部添加以下行。

pug
hr
p
  a(href=author.url+'/delete') Delete author

现在,该控件应该显示为一个链接,如下所示,位于作者详细信息页面上。

The Author details section of the Local library application. The left column has a vertical navigation bar. The right section contains the author details with a heading that has the Author's name followed by the life dates of the author and lists the books written by the author below it. There is a button labelled 'Delete Author' at the bottom.

它是什么样子的?

运行应用程序并在浏览器中打开https://127.0.0.1:3000/。然后选择所有作者链接,然后选择特定作者。最后,选择删除作者链接。

如果作者没有书籍,您将看到如下页面。按下删除后,服务器将删除作者并重定向到作者列表。

The Delete Author section of the Local library application of an author who does not have any books. The left column has a vertical navigation bar. The right section contains the author's name and life dates. There is the question "Do you really want to delete this author" with a button labeled 'Delete'.

如果作者有书籍,您将看到如下视图。然后,您可以从其详细信息页面删除书籍(一旦实现该代码!)。

The Delete Author section of the Local library application of an author who does have books under his name. The section contains the author's name and life dates of the author. There is a statement that reads "Delete the following books before attempting to delete this author" followed by the author's books. The list includes the titles of each book, as links, followed by a brief description in plain text.

注意:其他删除对象页面可以以相同的方式实现。我们将其留作挑战。

后续步骤