はじめに
Ruby on Railsは、Rubyで書かれたWebアプリケーションフレームワークであり、開発者にアプリケーション開発に対する意見を持ったアプローチを提供します。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.jsとnpmがローカルマシンまたは開発サーバーにインストールされています。このチュートリアルでは、Node.jsバージョン10.16.3とnpmバージョン6.9.0を使用しています。Ubuntu 18.04にNode.jsとnpmをインストールする手順については、「PPAを使用したインストール」セクションの手順に従ってください。Ubuntu 18.04にNode.jsをインストールする方法に関する指示に従ってください。
- ローカルマシンまたは開発サーバーにRuby、rbenv、およびRailsがインストールされています。Ubuntu 18.04にRuby on Railsをrbenvでインストールする手順は、1〜4のステップに従ってください。このチュートリアルでは、Rubyバージョン2.5.1、rbenvバージョン1.1.2、およびRailsバージョン5.2.3を使用しています。
- SQLiteがインストールされ、基本的なシャーク情報アプリケーションが作成されています。Ruby on Railsアプリケーションを構築する方法に指示に従ってください。
ステップ1 — ネストされたモデルのスキャフォールディング
アプリケーションは、Active Recordの関連付けを活用して、Shark
モデルとPost
モデルの関係を構築します:投稿は特定のサメに属し、各サメは複数の投稿を持つことができます。したがって、Shark
モデルとPost
モデルは、belongs_to
およびhas_many
の関連付けを介して関連付けられます。
このようにアプリケーションを構築するための最初のステップは、Post
モデルと関連するリソースを作成することです。これを行うには、rails generate scaffold
コマンドを使用します。このコマンドにより、モデル、データベースのスキーマを変更するためのデータベースマイグレーション、コントローラ、標準の作成、読み取り、更新、削除(CRUD)操作を管理するための完全なビューのセット、パーシャル、ヘルパー、テストのテンプレートが生成されます。これらのリソースを変更する必要がありますが、scaffold
コマンドを使用すると、出発点として使用できる構造が生成されるため、時間とエネルギーを節約できます。
まず、前提条件で作成したRailsプロジェクトのsharkapp
ディレクトリにいることを確認してください:
次のコマンドを使用してPost
リソースを作成します:
rails generate scaffold Post body:text shark:references
body:text
を使用して、Railsにposts
データベーステーブル内にbody
フィールドを含めるように指示しています。このテーブルはPost
モデルにマップされます。また、:references
キーワードを含めています。これにより、Shark
モデルとPost
モデルの間に関連を設定します。具体的には、sharks
データベース内の各サメエントリを表す外部キーがposts
データベースに追加されます。
コマンドを実行した後、Railsがアプリケーションに生成したリソースを確認する出力が表示されます。次に進む前に、モデルとデータベーステーブルの間に存在する関係を確認するために、データベースマイグレーションファイルを確認できます。以下のコマンドを使用してファイルの内容を確認します。ここで示されているタイムスタンプを、自分のマイグレーションファイルのものに置き換えてください:
次の出力が表示されます:
Outputclass 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
Outputclass Post < ApplicationRecord
belongs_to :shark
end
belongs_to
関連付けは、宣言されたモデルの単一のインスタンスが指定されたモデルの単一のインスタンスに属する関係を設定します。私たちのアプリケーションの場合、これは単一の投稿が単一のサメに属することを意味します。
この関係を設定するだけでなく、rails generate scaffold
コマンドは、How To Build a Ruby on Rails Applicationのステップ3で行ったように、ポストのためのルートとビューも作成しました。
これは便利な始まりですが、モデルとルートの間の関係が望ましいように機能するようにするには、いくつかの追加のルーティングを設定し、Shark
モデルのActive Record関連付けを強固にする必要があります。
ステップ2 — 親モデルのネストされたルートと関連付けの指定
Railsは、rails generate scaffold
コマンドの:references
キーワードのおかげで、Post
モデルにbelongs_to
関連付けをすでに設定していますが、この関係が正しく機能するようにするには、Shark
モデルにhas_many
関連付けを指定する必要があります。また、Railsが与えたデフォルトのルーティングを変更して、ポストリソースをシャークリソースの子にします。
has_many
アソシエーションを Shark
モデルに追加するには、nano
またはお好きなエディターで app/models/shark.rb
を開いてください。
次の行をファイルに追加して、サメと投稿の関係を確立します:
class Shark < ApplicationRecord
has_many :posts
validates :name, presence: true, uniqueness: true
validates :facts, presence: true
end
ここで考慮する価値があるのは、特定のサメが削除された場合に投稿がどうなるかです。おそらく、削除されたサメに関連付けられた投稿がデータベースに残るのは望ましくないでしょう。特定のサメに関連付けられた投稿が削除されるようにするには、関連付けに dependent
オプションを含めることができます。
次のコードをファイルに追加して、特定のサメの destroy
アクションが関連付けられた投稿を削除するようにします:
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
ファイルを開きます:
現在、ファイルは次のようになっています:
Rails.application.routes.draw do
resources :posts
resources :sharks
root 'sharks#index'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
現在のコードは、ルート間の独立した関係を確立していますが、私たちが表現したいのはサメとそれに関連する投稿の 依存関係です。
ルート宣言を更新して、:sharks
を:posts
の親にします。 ファイル内のコードを以下のように更新してください:
Rails.application.routes.draw do
resources :sharks do
resources :posts
end
root 'sharks#index'
# このファイル内で利用可能なDSLの詳細については、http://guides.rubyonrails.org/routing.html を参照してください
end
編集が完了したら、ファイルを保存して閉じてください。
これらの変更を行ったら、posts
コントローラを更新してください。
ステップ3 — Postsコントローラの更新
モデル間の関連付けにより、特定のsharksに関連付けられた新しい投稿インスタンスを作成するために使用できるメソッドが提供されます。 これらのメソッドを使用するには、postsコントローラにそれらを追加する必要があります。
postsコントローラファイルを開いてください:
現在、ファイルは次のようになっています:
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
# GET /posts
# GET /posts.json
def index
@posts = Post.all
end
# GET /posts/1
# GET /posts/1.json
def show
end
# GET /posts/new
def new
@post = Post.new
end
# GET /posts/1/edit
def edit
end
# POST /posts
# POST /posts.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 /posts/1
# PATCH/PUT /posts/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
# DELETE /posts/1
# DELETE /posts/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
このコントローラーのメソッドは、関連するPost
クラスのインスタンスと一緒に動作します。たとえば、new
メソッドはPost
クラスの新しいインスタンスを作成し、index
メソッドはクラスのすべてのインスタンスを取得し、set_post
メソッドはfind
とparams
を使用して特定の投稿をid
で選択します。 ただし、投稿インスタンスを特定のサメインスタンスに関連付けたい場合は、このコードを変更する必要があります。現在、Post
クラスは独立したエンティティとして機能しています。
私たちの変更は、2つのものを使用します:
belongs_to
およびhas_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
メソッドの上に次のメソッドを追加してください:
. . .
private
def get_shark
@shark = Shark.find(params[:shark_id])
end
# コールバックを使用して、アクション間で共通のセットアップや制約を共有します。
. . .
次に、ファイルの先頭に、既存のフィルターの前に対応するフィルターを追加してください:
class PostsController < ApplicationController
before_action :get_shark
これにより、get_shark
がファイルで定義されている各アクションの前に実行されるようになります。
次に、この@shark
インスタンスを使用してindex
メソッドを書き換えることができます。 Post
クラスのすべてのインスタンスを取得する代わりに、このメソッドは特定のシャークインスタンスに関連するすべての投稿インスタンスを返すようにしたいと思います。
index
メソッドを次のように変更してください:
. . .
def index
@posts = @shark.posts
end
. . .
new
メソッドも同様の修正が必要です。これは、新しい投稿インスタンスが特定のシャークに関連付けられるようにする必要があります。これを実現するには、build
メソッドとローカルの@shark
インスタンス変数を使用できます。
new
メソッドを次のように変更してください:
. . .
def new
@post = @shark.posts.build
end
. . .
このメソッドは、get_shark
メソッドから特定のシャークインスタンスに関連付けられた投稿オブジェクトを作成します。
次に、new
に最も密接に関連するメソッドであるcreate
メソッドに取り組みます。 create
メソッドは、ユーザーがnew
フォームに入力したパラメータを使用して新しい投稿インスタンスを構築し、エラーがない場合はそのインスタンスを保存し、ルートヘルパーを使用してユーザーを新しい投稿を見ることができる場所にリダイレクトします。エラーの場合は、new
テンプレートを再度レンダリングします。
create
メソッドを以下のように更新してください:
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
インスタンス変数を使用しています。この変数はどこから来るのでしょうか?
ファイルの上部にあるフィルターを見てみましょう。自動生成された2番目のbefore_action
フィルターは、答えを提供しています:
class PostsController < ApplicationController
before_action :get_shark
before_action :set_post, only: [:show, :edit, :update, :destroy]
. . .
update
メソッド(show
、edit
、およびdestroy
と同様に)は、set_post
メソッドから@post
変数を取得します。このメソッドは、他のprivate
メソッドとともにget_shark
メソッドの下にリストされています。現在のこのメソッドは次のようになっています:
. . .
private
. . .
def set_post
@post = Post.find(params[:id])
end
. . .
ファイルの他の箇所で使用しているメソッドに合わせて、このメソッドを変更する必要があります。ここではbuild
メソッドを考慮してください。モデル間の関連とそれらの関連によって利用可能になるメソッド(build
など)のおかげで、各ポストインスタンスは特定のサメに関連付けられたオブジェクトのコレクションの一部です。したがって、特定のポストをクエリする際には、特定のサメに関連付けられたポストのコレクションをクエリするのが理にかなっています。
set_post
を以下のように更新してください:
. . .
private
. . .
def set_post
@post = @shark.posts.find(params[:id])
end
. . .
Post
クラスの特定のインスタンスをid
で検索する代わりに、特定のサメに関連付けられたポストのコレクションで一致するid
を検索します。
その方法が更新されたので、update
とdestroy
メソッドを見てみることができます。
update
メソッドは、set_post
から@post
インスタンス変数を利用し、ユーザーがedit
フォームに入力したpost_params
と共に使用します。成功した場合は、Railsに特定のサメに関連付けられた投稿のindex
ビューにユーザーを送り返すことを望みます。エラーの場合、Railsは再びedit
テンプレートをレンダリングします。
この場合、必要な変更はredirect_to
ステートメントだけです。成功した更新を処理するために、shark_post_path(@shark)
にリダイレクトするように更新してください。これにより、選択したサメの投稿のindex
ビューにリダイレクトされます。
. . .
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)
にリダイレクトするように更新してください。
. . .
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
. . .
これが最後の変更です。これで、次のような投稿コントローラファイルができました:
class PostsController < ApplicationController
before_action :get_shark
before_action :set_post, only: [:show, :edit, :update, :destroy]
# GET /posts
# GET /posts.json
def index
@posts = @shark.posts
end
# GET /posts/1
# GET /posts/1.json
def show
end
# GET /posts/new
def new
@post = @shark.posts.build
end
# GET /posts/1/edit
def edit
end
# POST /posts
# POST /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
# PATCH/PUT /posts/1
# PATCH/PUT /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
# DELETE /posts/1
# DELETE /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
# Use callbacks to share common setup or constraints between actions.
def set_post
@post = @shark.posts.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def post_params
params.require(:post).permit(:body, :shark_id)
end
end
コントローラーは、情報がビューテンプレートからデータベースへ、またはその逆にどのように渡されるかを管理します。当社のコントローラーは現在、投稿が特定のサメに関連付けられているShark
およびPost
モデルの関係を反映しています。ユーザーが特定のサメに関する投稿情報を入力および変更する場所であるビューテンプレートを変更できます。
ステップ4 — ビューの変更
ビューテンプレートの変更には、投稿に関連するテンプレートを変更するだけでなく、サメのshow
ビューも含まれます。特定のサメに関連付けられた投稿をユーザーが表示できるようにしたいからです。
まず、投稿の基本的なテンプレートであるform
パーシャルを変更します。それを開きましょう。
form_with
フォームヘルパーに post
モデルだけでなく、shark
モデルも渡すようにします。ただし、post
を子リソースとして設定します。
ファイルの最初の行を以下のように変更して、shark と post リソースの関係を反映します:
<%= form_with(model: [@shark, post], local: true) do |form| %>
. . .
次に、ビュー内で関連する shark の shark_id
をリストするセクションを削除します。これはビュー内で必要な情報ではないためです。
最終的なフォームは、最初の行を編集し、削除された shark_id
セクションを含まない状態で以下のようになります:
<%= 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 %>
編集が完了したら、ファイルを保存して閉じます。
次に、特定の shark に関連する投稿を表示する index
ビューを開きます。
rails generate scaffold
コマンドのおかげで、Rails はほとんどのテンプレートを生成しました。これには、各投稿の body
フィールドとその関連する shark
を表示するテーブルが含まれています。
ただし、このテンプレートも他の修正したコードと同様に、投稿を独立したエンティティとして扱っています。代わりに、モデル間の関連性やこれらの関連性によって提供されるコレクションやヘルパーメソッドを活用したいと考えています。
テーブルの本文で、以下の更新を行います:
まず、post.shark
を post.shark.name
に更新し、テーブルに関連する shark の名前フィールドを含めます。これにより、shark オブジェクト自体の識別情報ではなく、関連する shark の名前フィールドが表示されます。
. . .
<tbody>
<% @posts.each do |post| %>
<tr>
<td><%= post.body %></td>
<td><%= post.shark.name %></td>
. . .
次に、Show
リダイレクトを変更して、ユーザーが元のサメに戻る方法を求める可能性が高いため、関連するサメの show
ビューに直接ユーザーをリダイレクトします。Railsはコントローラで設定したインスタンス変数 @shark
をすべてのビューで利用できるようにするので、ここでそれを利用できます。リンクのテキストも Show
から Show Shark
に変更して、ユーザーがその機能をより理解しやすくします。
この行を次のように更新します:
. . .
<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
行を次のように更新します:
. . .
<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
リンクに類似した変更を加えて、文字列内の機能を更新し、shark
と post
リソースを追加します。
. . .
<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)
ルーティングヘルパーを使用するように更新します:
. . .
<%= link_to 'New Post', new_shark_post_path(@shark) %>
完成したファイルは次のようになります:
<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
を開いてください。
ファイルの一番下にあるlink_to
参照を更新し、shark_posts_path(@shark)
ヘルパーを使用してください。
. . .
<%= link_to 'Back', shark_posts_path(@shark) %>
この変更が完了したら、ファイルを保存して閉じてください。
次に、edit
テンプレートを開いてください。
Back
パスに加えて、ネストされたリソースを反映するようにShow
を更新します。ファイルの最後の2行を次のように変更してください。
. . .
<%= link_to 'Show', [@shark, @post] %> |
<%= link_to 'Back', shark_posts_path(@shark) %>
ファイルを保存して閉じてください。
次に、show
テンプレートを開いてください。
nano app/views/posts/show.html.erb
ファイルの一番下にあるEdit
とBack
のパスを編集してください。
. . .
<%= link_to 'Edit', edit_shark_post_path(@shark, @post) %> |
<%= link_to 'Back', shark_posts_path(@shark) %>
編集が完了したら、ファイルを保存して閉じてください。
最後のステップとして、個々のサメに対して投稿が表示されるように、サメのshow
ビューを更新します。今すぐそのファイルを開いてください。
ここでの編集内容は、フォームにPosts
セクションを追加し、ファイルの一番下にAdd Post
リンクを追加することです。
以下は、特定のサメに関連付けられた投稿コレクション内の各インスタンスを反復処理し、各投稿の本文を出力する新しいセクションをFacts
セクションの下に追加します。
次に、フォームのFacts
セクションの下に、ファイルの最下部にリダイレクトを追加するコードを追加します。
. . .
<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) %> |
. . .
次に、特定のサメの新しい投稿を追加できるように、新しいリダイレクトを追加します。
. . .
<%= 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
モデルが定義されているファイルを開いてください:
ここでは、投稿が空白でないこと、および他のユーザーが投稿した可能性のあるコンテンツと重複しないことを確認したいと思います。これを実現するために、以下の行をファイルに追加してください:
class Post < ApplicationRecord
belongs_to :shark
validates :body, presence: true, uniqueness: true
end
編集が完了したら、ファイルを保存して閉じてください。
この最後の変更を行ったことで、マイグレーションを実行してアプリケーションをテストする準備が整いました。
まず、マイグレーションを実行します:
次に、サーバーを起動します。ローカルで作業している場合は、次のコマンドを実行してください:
開発サーバーで作業している場合は、代わりに次のコマンドを実行してください:
アプリケーションのルートに移動し、http://localhost:3000
またはhttp://your_server_ip:3000
にアクセスしてください。
前提としてのRailsプロジェクトチュートリアルでは、Great Whiteサメのエントリの追加と編集を進めました。それ以上のサメを追加していない場合、アプリケーションのランディングページは次のようになります:
表示をクリックしてください。次に、グレートホワイトの名前の横に表示されます。これにより、このサメのshow
ビューに移動します。サメの名前とその事実、そして内容のない投稿ヘッダーが表示されます。このフォームのこの部分を埋めるために投稿を追加しましょう。
投稿を追加をクリックしてください。これにより、投稿ヘッダーの下に新規投稿を選択できる投稿index
ビューに移動します。
ステップ6のRuby on Railsアプリケーションの構築方法で設定した認証メカニズムのおかげで、新しいセッションを作成したかどうかに応じて、そのステップで作成したユーザー名とパスワードで認証を求められる場合があります。
新規投稿をクリックして、投稿のnew
テンプレートに移動します。
本文フィールドに、「これらのサメは怖いです!」と入力してください。
投稿を作成をクリックします。これにより、このサメに属するすべての投稿のindex
ビューにリダイレクトされます。
投稿リソースが機能するようになったので、データ検証をテストして、データベースに保存されるのは必要なデータだけであることを確認できます。
index
ビューから新規投稿をクリックします。新しいフォームの本文フィールドに再度「これらのサメは怖いです!」と入力してみてください。
投稿を作成をクリックしてください。以下のエラーが表示されます:
戻るをクリックして、メインの投稿ページに戻ります。
他の検証をテストするには、新規投稿を再度クリックしてください。投稿を空白のままにして投稿を作成をクリックします。以下のエラーが表示されます:
ネストされたリソースと検証が正常に機能するようになったので、これで追加の開発の出発点として使用できる動作するRailsアプリケーションが完成しました。
結論
Railsアプリケーションが準備できたので、スタイリングや他のフロントエンドコンポーネントの開発などに取り組むことができます。ルーティングやネストされたリソースについて詳しく学びたい場合は、Railsドキュメントが良い出発点です。
アプリケーションにフロントエンドフレームワークを統合する方法について詳しく学びたい場合は、Ruby on RailsプロジェクトにReactフロントエンドを設定する方法をご覧ください。