如何为Ruby on Rails应用程序创建嵌套资源

介绍

Ruby on Rails 是一个用 Ruby 编写的网络应用框架,为开发人员提供了一种具有主观倾向的应用开发方法。使用 Rails,开发人员能够获得:

  • 处理路由、状态数据和资源管理等方面的约定。
  • A firm grounding in the model-view-controller (MCV) architectural pattern, which separates an application’s logic, located in models, from the presentation and routing of application information.

随着您在 Rails 应用程序中添加复杂性,您可能会与多个模型一起工作,这些模型代表了应用程序的业务逻辑,并与数据库进行交互。添加相关模型意味着在它们之间建立有意义的关系,这将影响信息如何通过应用程序的控制器传递以及如何通过视图捕获并呈现给用户。

在本教程中,您将在一个提供关于鲨鱼的事实的现有 Rails 应用程序上构建。该应用程序已经有一个处理鲨鱼数据的模型,但是您将添加一个关于个别鲨鱼帖子的嵌套资源。这将允许用户构建有关个别鲨鱼的更广泛的思想和观点。

先决条件

要按照本教程,您需要:

  • A local machine or development server running Ubuntu 18.04. Your development machine should have a non-root user with administrative privileges and a firewall configured with ufw. For instructions on how to set this up, see our Initial Server Setup with Ubuntu 18.04 tutorial.
  • 您的本地计算机或开发服务器上安装了Node.jsnpm。本教程使用Node.js版本10.16.3和npm版本6.9.0。有关在Ubuntu 18.04上安装Node.js和npm的指导,请按照在Ubuntu 18.04上安装Node.js中“使用PPA安装”的说明进行操作。
  • 在您的本地计算机或开发服务器上安装了Ruby、rbenv和Rails,并按照如何在Ubuntu 18.04上使用rbenv安装Ruby on Rails中的步骤1-4进行操作。本教程使用Ruby 2.5.1,rbenv 1.1.2和Rails 5.2.3
  • 安装了SQLite,并创建了一个基本的shark信息应用程序,按照如何构建Ruby on Rails应用程序中的说明进行操作。

步骤1 —— 创建嵌套模型的脚手架

我们的应用程序将利用Active Record的关联来建立SharkPost模型之间的关系:帖子将属于特定的鲨鱼,每条鲨鱼都可以有多条帖子。因此,我们的SharkPost模型将通过belongs_tohas_many关联相关联。

以这种方式构建应用程序的第一步将是创建一个Post模型和相关资源。为此,我们可以使用rails generate scaffold命令,它将为我们提供一个模型,一个用于更改数据库架构的数据库迁移,一个控制器,用于管理标准的创建、读取、更新和删除(CRUD)操作的一整套视图,以及用于partials、helpers和tests的模板。我们需要修改这些资源,但是使用scaffold命令会节省我们一些时间和精力,因为它生成了一个我们可以用作起点的结构。

首先,确保您位于先决条件中创建的Rails项目的sharkapp目录中:

  1. cd sharkapp

使用以下命令创建您的Post资源:

rails generate scaffold Post body:text shark:references

通过body:text,我们告诉Rails在posts数据库表中包含一个body字段,这个表映射到Post模型。我们还包括:references关键字,它建立了SharkPost模型之间的关联。具体来说,这将确保在posts数据库中添加一个表示每个sharks数据库条目的外键。

一旦你运行了命令,你会看到输出,确认了Rails为应用程序生成的资源。在继续之前,你可以检查你的数据库迁移文件,查看你的模型和数据库表之间现在存在的关系。使用以下命令查看文件的内容,确保用你自己迁移文件上的时间戳替换这里显示的内容:

  1. cat db/migrate/20190805132506_create_posts.rb

你将看到以下输出:

Output
class CreatePosts < ActiveRecord::Migration[5.2] def change create_table :posts do |t| t.text :body t.references :shark, foreign_key: true t.timestamps end end end

如你所见,表中包括一个用于鲨鱼外键的列。这个键的形式将是model_name_id — 在我们的案例中是shark_id

Rails还在其他地方建立了模型之间的关系。使用以下命令查看新生成的Post模型:

cat app/models/post.rb
Output
class Post < ApplicationRecord belongs_to :shark end

belongs_to关联建立了模型之间的关系,其中声明模型的单个实例属于命名模型的单个实例。在我们的应用程序中,这意味着单个帖子属于单个鲨鱼。

除了设置这个关系外,rails generate scaffold命令还为帖子创建了路由和视图,就像我们在步骤3中为鲨鱼资源所做的那样。这是一个有用的起点,但为了使模型和路由之间的关系按预期工作,我们需要配置一些额外的路由并巩固Shark模型的Active Record关联。

步骤2 —— 为父模型指定嵌套路由和关联

由于rails generate scaffold命令中的:references关键字,Rails已经在我们的Post模型中设置了belongs_to关联,但为了使该关系正常工作,我们还需要在Shark模型中指定一个has_many关联。我们还需要对Rails提供的默认路由进行更改,以使帖子资源成为鲨鱼资源的子资源。

要向 Shark 模型添加 has_many 关联,请使用 nano 或您喜欢的编辑器打开 app/models/shark.rb :

  1. nano app/models/shark.rb

将以下行添加到文件中,以建立鲨鱼和帖子之间的关系:

~/sharkapp/app/models/shark.rb
class Shark < ApplicationRecord
  has_many :posts
  validates :name, presence: true, uniqueness: true
  validates :facts, presence: true
end

这里值得考虑的一件事是,一旦删除了特定的鲨鱼,帖子会发生什么。我们可能不希望与已删除鲨鱼相关联的帖子在数据库中持续存在。为了确保与给定鲨鱼相关联的任何帖子在删除该鲨鱼时被删除,我们可以在关联中包含 dependent 选项。

添加以下代码到文件中,以确保给定鲨鱼上的 destroy 操作会删除任何关联的帖子:

~/sharkapp/app/models/shark.rb
class Shark < ApplicationRecord
  has_many :posts , dependent: :destroy
  validates :name, presence: true, uniqueness: true
  validates :facts, presence: true
end

完成这些更改后,请保存并关闭文件。如果您使用 nano ,您可以按 CTRL+X Y ,然后按 ENTER 来执行此操作。

接下来,打开您的 config/routes.rb 文件以修改您的资源路由之间的关系:

  1. nano config/routes.rb

当前,该文件如下所示:

~/sharkapp/config/routes.rb
Rails.application.routes.draw do
  resources :posts 
  resources :sharks

  root 'sharks#index'
  # 有关此文件中可用DSL的详细信息,请参见http://guides.rubyonrails.org/routing.html
end

当前的代码建立了我们路由之间的独立关系,但我们想要表达的是鲨鱼及其关联帖子之间的依赖关系

让我们更新我们的路由声明,将:sharks设置为:posts的父级。更新文件中的代码如下:

~/sharkapp/config/routes.rb
Rails.application.routes.draw do
  resources :sharks do
    resources :posts
end
  root 'sharks#index'
  # 有关此文件中可用 DSL 的详细信息,请参阅 http://guides.rubyonrails.org/routing.html
end

编辑完成后,请保存并关闭文件。

完成这些更改后,您可以继续更新您的posts控制器。

第三步 — 更新帖子控制器

我们模型之间的关联提供了我们可以使用的方法,用于创建与特定鲨鱼关联的新帖子实例。为了使用这些方法,我们需要将它们添加到我们的帖子控制器中。

打开帖子控制器文件:

  1. nano app/controllers/posts_controller.rb

当前,文件如下所示:

~/sharkapp/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :set_post, only: [:show, :edit, :update, :destroy]

  #获取/帖子
  #获取/帖子.json
  def index
    @posts = Post.all
  end

  #获取/帖子/1
  #获取/帖子/1.json
  def show
  end

  #获取/帖子/new
  def new
    @post = Post.new
  end

  #获取/帖子/1/edit
  def edit
  end

  #发布/帖子
  #发布/帖子.json
  def create
    @post = Post.new(post_params)

    respond_to do |format|
      if @post.save
        format.html { redirect_to @post, notice: 'Post was successfully created.' }
        format.json { render :show, status: :created, location: @post }
      else
        format.html { render :new }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  #PATCH / PUT /帖子/ 1
  #PATCH / PUT /帖子/ 1.json
  def update
    respond_to do |format|
      if @post.update(post_params)
        format.html { redirect_to @post, notice: 'Post was successfully updated.' }
        format.json { render :show, status: :ok, location: @post }
      else
        format.html { render :edit }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  #删除/帖子/ 1
  #删除/帖子/ 1.json
  def destroy
    @post.destroy
    respond_to do |format|
      format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    #使用回调函数在操作之间共享常见的设置或约束
    def set_post
      @post = Post.find(params[:id])
    end

    #永远不要相信来自可怕的互联网的参数,只允许通过白名单
    def post_params
      params.require(:post).permit(:body, :shark_id)
    end
end

像我们的sharks控制器一样,这个控制器的方法与关联的 Post 类的实例一起工作。例如, new 方法创建 Post 类的新实例, index 方法获取所有类的实例, set_post 方法使用 find params id 选择特定的帖子。但是,如果我们希望我们的帖子实例与特定的鲨鱼实例关联,则需要修改此代码,因为 Post 类当前正在作为独立实体运行。

我们的修改将利用两件事情:

  • 当我们向模型添加belongs_tohas_many关联时,我们可以使用的方法。具体来说,现在我们可以通过在Shark模型中定义的has_many关联来访问build方法。这个方法将允许我们使用存在于我们的posts数据库中的shark_id外键来创建与特定鲨鱼对象相关联的帖子对象集合。
  • 当我们创建了嵌套的posts路由时,可以使用的路由和路由助手。要查看在资源之间创建嵌套关系时可用的示例路由的完整列表,请参阅Rails文档。现在,我们只需要知道对于每个特定的鲨鱼,比如sharks/1,将会有一个与该鲨鱼相关联的帖子的路由:sharks/1/posts。还将有像shark_posts_path(@shark)edit_sharks_posts_path(@shark)这样的路由助手,用于引用这些嵌套路由。

在文件中,我们将首先编写一个名为get_shark的方法,在控制器中的每个操作之前运行此方法。此方法将通过shark_id查找鲨鱼实例,并创建一个名为@shark的局部实例变量。有了这个变量,我们就可以在其他方法中将帖子关联到特定的鲨鱼。

在文件底部的其他private方法之上,添加以下方法:

~/sharkapp/controllers/posts_controller.rb
. . . 
private
  def get_shark
@shark = Shark.find(params[:shark_id])
end
  # 使用回调函数在操作之间共享公共设置或约束。
. . . 

接下来,在现有过滤器之前,将相应的过滤器添加到文件的顶部

~/sharkapp/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :get_shark

这将确保get_shark在文件中定义的每个操作之前运行。

接下来,您可以使用这个@shark实例来重写index方法。我们不再想要这个方法抓取Post类的所有实例,而是想要这个方法返回与特定shark实例关联的所有post实例。

修改index方法如下:

~/sharkapp/controllers/posts_controller.rb
. . .
  def index
    @posts = @shark.posts
  end
. . .

new方法将需要类似的修改,因为我们希望新的post实例与特定的shark关联。为了实现这一点,我们可以利用build方法以及我们的本地@shark实例变量。

new方法更改为如下所示:

~/sharkapp/controllers/posts_controller.rb
. . . 
  def new
    @post = @shark.posts.build
  end
. . . 

此方法创建一个与get_shark方法中特定shark实例关联的post对象。

接下来,我们将解决与new最紧密相关的方法:createcreate方法执行两个操作:使用用户在new表单中输入的参数构建一个新的post实例,如果没有错误,则保存该实例并使用路由辅助程序将用户重定向到他们可以查看新post的位置。如果出现错误,则再次呈现new模板。

更新create方法如下所示:

~/sharkapp/controllers/posts_controller.rb
  def create
    @post = @shark.posts.build(post_params)

        respond_to do |format|
         if @post.save  
            format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully created.' }
            format.json { render :show, status: :created, location: @post }
         else
            format.html { render :new }
            format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

接下来,看一下update方法。这个方法使用了一个@post实例变量,该变量在方法本身中并没有明确设置。这个变量是从哪里来的呢?

看一下文件顶部的过滤器。第二个自动生成的before_action过滤器提供了答案:

~/sharkapp/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :get_shark
  before_action :set_post, only: [:show, :edit, :update, :destroy]
  . . .

update方法(就像showeditdestroy方法一样)从set_post方法获取一个@post变量。该方法,列在get_shark方法下面和其他private方法一起,目前看起来是这样的:

~/sharkapp/controllers/posts_controller.rb
. . . 
private
. . . 
  def set_post
    @post = Post.find(params[:id])
  end
. . .

与文件中其他地方使用的方法保持一致,我们需要修改这个方法,使@post指向与特定鲨鱼相关联的帖子集合中的一个特定实例。在这里要牢记build方法——由于我们的模型之间的关联以及这些关联带来的方法(如build),我们的每个帖子实例都是与特定鲨鱼关联的对象集合的一部分。因此,当查询特定帖子时,我们会查询与特定鲨鱼关联的帖子集合。

set_post更新为如下所示:

~/sharkapp/controllers/posts_controller.rb
. . . 
private
. . . 
  def set_post
    @post = @shark.posts.find(params[:id])
  end
. . .

我们不再通过id来查找整个Post类的特定实例,而是在与特定鲨鱼相关联的帖子集合中搜索匹配的id

随着该方法的更新,我们可以查看updatedestroy方法。

update方法利用了set_post中的@post实例变量,并将其与用户在edit表单中输入的post_params一起使用。在成功的情况下,我们希望Rails将用户重定向到与特定鲨鱼相关联的帖子的index视图。在出现错误的情况下,Rails将再次呈现edit模板。

在这种情况下,我们唯一需要做的更改是redirect_to语句,以处理成功的更新。将其更新为重定向到shark_post_path(@shark),这将重定向到所选鲨鱼帖子的index视图:

~/sharkapp/controllers/posts_controller.rb
. . . 
  def update
    respond_to do |format|
      if @post.update(post_params)
        format.html { redirect_to shark_post_path(@shark), notice: 'Post was successfully updated.' }
        format.json { render :show, status: :ok, location: @post }
      else
        format.html { render :edit }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end
. . .

接下来,我们将类似地更改destroy方法。将redirect_to方法更新为在成功的情况下重定向请求到shark_posts_path(@shark)

~/sharkapp/controllers/posts_controller.rb
. . . 
  def destroy
    @post.destroy
     respond_to do |format|
      format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully destroyed.' }
      format.json { head :no_content }
    end
  end
. . .

这是我们将要进行的最后一次更改。您现在有一个看起来像这样的帖子控制器文件:

~/sharkapp/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :get_shark
  before_action :set_post, only: [:show, :edit, :update, :destroy]

  # 获取/posts
  # 获取/posts.json
  def index
    @posts = @shark.posts
  end

  # 获取/posts/1
  # 获取/posts/1.json
  def show
  end

  # 获取/posts/new
  def new
    @post = @shark.posts.build
  end

  # 获取/posts/1/edit
  def edit
  end

  # 提交/posts
  # 提交/posts.json
  def create
    @post = @shark.posts.build(post_params)

        respond_to do |format|
         if @post.save  
            format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully created.' }
            format.json { render :show, status: :created, location: @post }
         else
            format.html { render :new }
            format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # 更新/posts/1
  # 更新/posts/1.json
  def update
    respond_to do |format|
      if @post.update(post_params)
        format.html { redirect_to shark_post_path(@shark), notice: 'Post was successfully updated.' }
        format.json { render :show, status: :ok, location: @post }
      else
        format.html { render :edit }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # 删除/posts/1
  # 删除/posts/1.json
  def destroy
    @post.destroy
    respond_to do |format|
      format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private

   def get_shark
     @shark = Shark.find(params[:shark_id])
   end
    # 使用回调函数来共享视图模板和数据库之间的公共设置或约束。
    def set_post
      @post = @shark.posts.find(params[:id])
    end

    # 不要相信来自可怕互联网的参数,只允许通过白名单。
    def post_params
      params.require(:post).permit(:body, :shark_id)
    end
end

控制器管理信息如何从视图模板传递到数据库,反之亦然。我们的控制器现在反映了我们的SharkPost模型之间的关系,其中帖子与特定的鲨鱼相关联。我们可以继续修改视图模板本身,这是用户将传入和修改关于特定鲨鱼的帖子信息的地方。

第4步 — 修改视图

我们的视图模板修改将涉及更改与帖子相关的模板,还将修改我们的鲨鱼show视图,因为我们希望用户看到与特定鲨鱼相关联的帖子。

让我们从我们的帖子的基础模板开始:在多个帖子模板中重复使用的form部分。现在打开该表单:

  1. nano app/views/posts/_form.html.erb

与其只传递 post 模型给 form_with 表单助手,我们将同时传递 sharkpost 模型,其中 post 被设置为子资源。

将文件的第一行更改为以下内容,反映我们的鲨鱼和帖子资源之间的关系:

~/sharkapp/views/posts/_form.html.erb
<%= form_with(model: [@shark, post], local: true) do |form| %>
. . . 

接下来,删除 列出相关鲨鱼的 shark_id 的部分,因为这在视图中并不是必要的信息。

完成编辑后的表单,包括我们对第一行的编辑以及没有删除的 shark_id 部分,将如下所示:

~/sharkapp/views/posts/_form.html.erb
<%= form_with(model: [@shark, post], local: true) do |form| %>
  <% if post.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved:</h2>

      <ul>
      <% post.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= form.label :body %>
    <%= form.text_area :body %>
  </div>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

完成编辑后保存并关闭文件。

接下来,打开将显示与特定鲨鱼关联的帖子的 index 视图:

  1. nano app/views/posts/index.html.erb

由于 rails generate scaffold 命令的帮助,Rails 已经生成了模板的大部分内容,包括显示每个帖子的 body 字段及其关联的 shark 的表格。

然而,与我们已经修改的其他代码一样,这个模板将帖子视为独立的实体,而我们希望利用模型之间的关联以及这些关联提供的集合和助手方法。

在表格的主体中进行以下更新:

首先,将 post.shark 更新为 post.shark.name,以便表格将包括关联鲨鱼的名称字段,而不是关于鲨鱼对象本身的标识信息:

~/sharkapp/app/views/posts/index.html.erb
. . . 
  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
. . . 

接下来,将Show重定向更改为直接将用户重定向到相关鲨鱼的show视图,因为他们很可能想要一种返回原始鲨鱼的方法。我们可以利用在控制器中设置的@shark实例变量,在这里,因为Rails使控制器中创建的实例变量对所有视图都可用。我们还将更改链接的文本,从Show更改为Show Shark,以便用户更好地理解其功能。

将此行更新为以下内容:

~/sharkapp/app/views/posts/index.html.erb
. . . 
  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
        <td><%= link_to 'Show Shark', [@shark] %></td>

在接下来的一行中,我们希望确保用户在编辑帖子时被路由到正确的嵌套路径。这意味着用户不会被重定向到posts/post_id/edit,而是将被重定向到sharks/shark_id/posts/post_id/edit。为此,我们将使用shark_post_path路由助手和我们的模型,Rails将把它们视为URL。我们还将更新链接文本以使其功能更清晰。

Edit行更新为以下内容:

~/sharkapp/app/views/posts/index.html.erb
. . . 
  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
        <td><%= link_to 'Show Shark', [@shark] %></td>
        <td><%= link_to 'Edit Post', edit_shark_post_path(@shark, post) %></td>

接下来,让我们对Destroy链接进行类似的更改,更新字符串中的功能,并添加我们的sharkpost资源:

~/sharkapp/app/views/posts/index.html.erb
. . . 
  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
        <td><%= link_to 'Show Shark', [@shark] %></td>
        <td><%= link_to 'Edit Post', edit_shark_post_path(@shark, post) %></td>
        <td><%= link_to 'Destroy Post', [@shark, post], method: :delete, data: { confirm: 'Are you sure?' } %></td>

最后,在表单底部,当用户想要创建新帖子时,我们将要更新New Post路径以将用户带到适当的嵌套路径。更新文件的最后一行以利用new_shark_post_path(@shark)路由助手:

~/sharkapp/app/views/posts/index.html.erb
. . . 
<%= link_to 'New Post', new_shark_post_path(@shark) %>

完成的文件将如下所示:

~/sharkapp/app/views/posts/index.html.erb
<p id="notice"><%= notice %></p>

<h1>Posts</h1>

<table>
  <thead>
    <tr>
      <th>Body</th>
      <th>Shark</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
        <td><%= link_to 'Show Shark', [@shark] %></td>
        <td><%= link_to 'Edit Post', edit_shark_post_path(@shark, post) %></td>
        <td><%= link_to 'Destroy Post', [@shark, post], method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Post', new_shark_post_path(@shark) %>

完成编辑后保存并关闭文件。

我们对帖子视图的其他编辑将不会像这么多,因为我们的其他视图使用了我们已经编辑过的form部分。然而,我们需要更新其他帖子模板中的link_to引用,以反映我们对form部分所做的更改。

打开app/views/posts/new.html.erb

  1. nano app/views/posts/new.html.erb

更新文件底部的link_to引用,使用shark_posts_path(@shark)助手:

~/sharkapp/app/views/posts/new.html.erb
. . . 
<%= link_to 'Back', shark_posts_path(@shark) %>

完成此更改后保存并关闭文件。

接下来,打开edit模板:

  1. nano app/views/posts/edit.html.erb

除了Back路径外,我们将更新Show以反映我们的嵌套资源。将文件的最后两行更改为以下内容:

~/sharkapp/app/views/posts/edit.html.erb
. . . 
<%= link_to 'Show', [@shark, @post] %> |
<%= link_to 'Back', shark_posts_path(@shark) %>

保存并关闭文件。

接下来,打开show模板:

nano app/views/posts/show.html.erb

对文件底部的EditBack路径进行以下编辑:

~/sharkapp/app/views/posts/edit.html.erb
. . .
<%= link_to 'Edit', edit_shark_post_path(@shark, @post) %> |
<%= link_to 'Back', shark_posts_path(@shark) %>

完成后保存并关闭文件。

作为最后一步,我们将更新我们鲨鱼的show视图,以便为单个鲨鱼显示帖子。现在打开该文件:

  1. nano app/views/sharks/show.html.erb

我们的编辑包括在表单中添加一个Posts部分和文件底部添加一个Add Post链接。

在给定鲨鱼的Facts下面,我们将添加一个新的部分,遍历与该鲨鱼关联的帖子集合中的每个实例,输出每个帖子的body

在表单的Facts部分下面,添加以下代码,并在文件底部的重定向之上:

~/sharkapp/app/views/sharks/show.html.erb
. . .
<p>
  <strong>Facts:</strong>
  <%= @shark.facts %>
</p>

<h2>Posts</h2>
<% for post in @shark.posts %>
    <ul>
      <li><%= post.body %></li>
  </ul>
<% end %>

<%= link_to 'Edit', edit_shark_path(@shark) %> |
. . . 

接下来,添加一个新的重定向,允许用户为这特定的鲨鱼添加一个新的帖子:

~/sharkapp/app/views/sharks/show.html.erb
. . .
<%= link_to 'Edit', edit_shark_path(@shark) %> |
<%= link_to 'Add Post', shark_posts_path(@shark) %> |
<%= link_to 'Back', sharks_path %>

完成编辑后保存并关闭文件。

现在,您已经对应用程序的模型、控制器和视图进行了更改,以确保帖子始终与特定的鲨鱼相关联。作为最后一步,我们可以在Post模型中添加一些验证,以确保将数据保存到数据库中的一致性。

步骤5 —— 添加验证并测试应用程序

在《步骤 5》中的《如何构建一个 Ruby on Rails 应用》,您已经向您的 Shark 模型添加了验证,以确保保存到 sharks 数据库中的数据的一致性和统一性。现在我们将采取类似的步骤来确保 posts 数据库的保证。

打开定义 Post 模型的文件:

  1. nano app/models/post.rb

在这里,我们希望确保帖子不为空,并且它们不会重复其他用户可能已发布的内容。为此,请在文件中添加以下行:

~/sharkapp/app/models/post.rb
class Post < ApplicationRecord
  belongs_to :shark
  validates :body, presence: true, uniqueness: true
end

完成编辑后保存并关闭文件。

完成最后的更改后,您可以运行迁移并测试应用程序。

首先,运行您的迁移:

  1. rails db:migrate

接下来,启动服务器。如果您是在本地工作,可以运行以下命令:

  1. rails s

如果您正在开发服务器上工作,请运行以下命令代替:

  1. rails s --binding=your_server_ip

导航到您应用程序的根目录,在 http://localhost:3000http://your_server_ip:3000

前提条件 Rails 项目教程已经引导您添加和编辑了一个 Great White 鲨鱼条目。如果您没有添加更多的鲨鱼,则应用程序的登陆页面将如下所示:

点击显示旁边的大白鲨的名称。这将带您到此鲨鱼的显示视图。您将看到鲨鱼的名称及其事实,以及一个没有内容的帖子标题。让我们添加一个帖子来填充表单的这一部分。

点击添加帖子,位于帖子标题下方。这将带您到帖子索引视图,在那里您将有机会选择新帖子

感谢您在步骤6中放置的身份验证机制如何构建Ruby on Rails应用程序,您可能会被要求使用您在该步骤中创建的用户名和密码进行身份验证,这取决于您是否已创建新会话。

点击新帖子,这将带您到您的帖子模板:

正文字段中输入“这些鲨鱼很可怕!”

点击创建帖子。您将被重定向到属于此鲨鱼的所有帖子的索引视图:

通过我们的帖子资源工作,我们现在可以测试我们的数据验证,以确保只有所需的数据保存到数据库中。

索引视图中,点击新帖子。在新表单的正文字段中,尝试再次输入“这些鲨鱼很可怕!”:

点击创建帖子。您将看到以下错误:

点击返回返回到主帖子页面。

要测试我们的其他验证,请再次点击新帖子。将帖子留空并点击创建帖子。您将看到以下错误:

有了正确的嵌套资源和验证,您现在拥有一个可以作为进一步开发的起点的工作中的Rails应用程序。

结论

有了您的Rails应用程序,您现在可以开始处理样式和开发其他前端组件。如果您想了解更多关于路由和嵌套资源的信息,可以查看Rails文档,这是一个很好的开始。

要了解如何将前端框架与您的应用程序集成,请查看如何使用React前端设置Ruby on Rails项目

Source:
https://www.digitalocean.com/community/tutorials/how-to-create-nested-resources-for-a-ruby-on-rails-application