Rails の new, create アクションを理解しよう

学習の目標

本章では、以下の内容を学習します。

  • new アクションの役割と動作原理を理解する
  • create アクションの処理フローを学ぶ
  • フォームの作成と送信の仕組みを理解する
  • form_with ヘルパーの基本的な使い方を習得する
  • リダイレクトと render の違いと使い分けを理解する

はじめに

前回は index, show アクションについて学びました。今回のレクチャーでは、新規データ作成に関わる new アクションと、create アクションについて詳しく解説していきます。

これらのアクションは、Webアプリケーションでユーザーからの入力を受け付け、データベースに保存するという重要な役割を担っています。フォームの仕組みと合わせて理解していきましょう。

newアクション - 新規作成用の空オブジェクトを用意

newアクションの役割

new アクションでは、新規投稿用の「空のモデルオブジェクト」を生成します。コード例は、次のようになります。

def new
  @post = Post.new
end

このシンプルなコードで、新しい空の Post オブジェクトを作成し、@post というインスタンス変数に格納しています。この時点では、データベースには何も保存されていません。

ここで生成された @post をフォーム画面 new.html.erb に渡すことで、フォーム部品に値を入れやすい形を作っています。

フォームビューの書き換え

投稿フォームでは form_with という重要なヘルパーメソッドを使います。scaffoldで作られた書き方がありますが、理解しやすい内容に書き換えてみましょう。

new.html.erb を開き、以下のように書き換えてください。

<h1>New post</h1>

<%= form_with(model: @post) do |form| %>
  <div>
    <%= form.label :title, style: "display: block" %>
    <%= form.text_field :title %>
  </div>

  <div>
    <%= form.label :body, style: "display: block" %>
    <%= form.text_area :body %>
  </div>

  <div>
    <%= form.submit %>
  </div>
<% end %>

<br>

<div>
  <%= link_to "Back to posts", posts_path %>
</div>

ここで、form_with という見慣れないヘルパーメソッド(Rails独自の書き方)が出てきました。この点について、詳しく見ていきましょう。

form_with の使い方とフォームの基本

form_with とは

Railsでは、フォームを作成する際に form_with というヘルパーメソッドを使います。これを使うと、データの送信先(どのアクションに送るか)や、どのモデルのデータを扱うかを簡単に指定できます。

フォームのコードの1行目に注目してみましょう。

<%= form_with(model: @post) do |form| %>

ここでは model: @post と書いているので、このフォームは @post(Postモデルのオブジェクト)に関連付けられます。新規作成時は create アクション、編集時は update アクションへ送信されるフォームになります。

form_with に渡すモデルが、newアクションで作成された新しいオブジェクトであれば、フォームは新規作成用のフォームになります。一方、editアクションで取得した既存の投稿データが入ったオブジェクトであれば、フォームは編集用のフォームになります。

このような特徴を持っているため、実は newアクションと editアクションで同じフォームを使うことができるのです。Railsがモデルの状態を見て、適切な動作を自動的に決定してくれます。

ブロック変数 form とは?

do |form| の部分は「ブロック」です。ブロック変数の名前は自由ですが、多くの場合 formf と書かれることが多いです。

この form はフォーム全体を表すオブジェクトで、フォーム内の各パーツを生成するためのヘルパーメソッドを呼び出す役割を持ちます。

フォーム部品の作成

<%= form.label :title, style: "display: block" %>
<%= form.text_field :title %>
<%= form.text_area :body %>
  • form.label :title は入力欄のラベル(見出し)を作ります。
  • form.text_field :title は1行のテキスト入力欄を作ります(タイトルなど短い文字列の入力に向いています)。
  • form.text_area :body は複数行のテキスト入力欄を作ります(本文など長文の入力に向いています)。

フォーム送信ボタン

<%= form.submit %>

これはフォームの送信ボタンを生成するためのヘルパーメソッドです。デフォルトのボタンラベルは「Create Post」ですが、次のように書けばボタンのラベルを変えられます。

<%= form.submit "投稿する" %>

createアクション - データベースへの保存を実行

createアクションの役割

create アクションでは、実際にモデルにデータを保存する処理を行います。フォームから送信されたパラメータ(params[:post])をもとに、Post.newでインスタンスを作成し、@post.saveによってデータベースに書き込みます。

def create
  @post = Post.new(post_params)

  if @post.save
    redirect_to post_path(@post), notice: "Post was successfully created."
  else
    render :new
  end
end

# ...略

private

def post_params
  params.require(:post).permit(:title, :body)
end

post_paramsストロングパラメータと呼ばれる仕組みで、セキュリティのためにtitlebodyだけを受け取るように制限しています。これにより、悪意ある送信から守ることができます。

また、@post.save という部分では、作成された @post、つまり投稿データをデータベースに保存しています。

保存に成功した場合

保存に成功したら @post.save の結果は true になるので、if文の最初のブロックに入り、以下の1行が実行されます。

redirect_to post_path(@post), notice: "Post was successfully created."

まず、redirect_to post_path(@post) という部分は詳細画面(showアクション)に移動させる、という意味になります。別ページへ移動させることは「リダイレクト」と呼ばれます。

この記述は「お決まりの書き方」として覚えていただいてOKです。今回、コントローラは posts_controller という名前でしたね。その「posts」という部分が鍵になっています。

showアクションは「複数」ではなく「単体」の投稿データに関するページなので「s」を外した「post」という名前をまず使っています。そしてRailsでは、そういった単体の投稿へのページを表す道筋、つまりパスを「post_path」と表記したメソッドを使います。

投稿の詳細ページを開く対象となる投稿データをどれか一つ指定しなければならないので、post_path に対して、対象となる投稿データである @post を引数として渡しています。

このように、とあるページへの道作り(ルーティング)を助けてくれるようなメソッドをヘルパーメソッドと呼びます。他にもありますが、まずはこういったルーティングに関するヘルパーメソッドから覚えておきましょう。

また、リダイレクトさせた後、投稿に成功した旨を表示するための記述がこの部分です。

notice: "Post was successfully created."

これは、app/views/posts/show.html.erb における以下の部分で使われます。scaffoldで作った場合、この記述が自動で追加されているはずです。

<p style="color: green"><%= notice %></p>

コントローラで notice という変数が作られ、メッセージを埋め込み、そのメッセージをビューで表示するといった流れになっています。ただし、リロードすると消えるかと思います。

このように、一時的に表示させるためのメッセージをフラッシュメッセージと呼びますので、覚えておきましょう。こういった一時的な表示のされ方はRailsが裏側でうまいことやってくれているためですので、「こういうもの」と覚えてしまって大丈夫です。

保存に失敗した場合

@post.save という記述では入力データをもとに投稿データを保存していますが、これに失敗した場合はどうなるでしょうか?

まず、@post.save の結果は false となります。そして createアクションでは if @post.save と書いてあるので、else文の処理が実行されることになります。

render :new

データ保存成功時は redirect_to と書いていましたが、今回は render となっていますね。これらの違いは以下のようになります。

  • redirect_to:リダイレクト先のページを開くためのアクションを実行する
  • render :xxx:別なアクションは呼び出さずに xxx.html.erb を開く

結果的に開かれるページは同じビュー(xxx.html.erb)を使っているわけなのですが、renderredirect_to と大きく違うのは、ビューに対応するアクションを呼び出さないという点です。

createアクションでは render メソッドで new.html.erb をそのまま開いているので、データベースに保存、つまり save しようとしていた @post はそのまま使いまわされます。その @postnew.html.erb で表示すると、入力していた文字などをそのまま表示することができます。

一方、redirect_to メソッドを使って newアクションを呼び出すことで newのビューを表示してしまうと、newアクションにより @post が初期化されてしまうので、まっさらになってしまいます。

例えばこれが氏名・住所・プロフィールなどを一気に記入するようなフォームだとしたら、ゼロから入力するのは面倒ですよね。そのため、newアクションをゼロから実行する redirect_to ではなく、@post を使い回してビューを表示できる render というメソッドを使っているのです。

データの流れを理解する

ここで、newアクションからcreateアクションまでの一連の流れをおさらいしておきましょう。

  1. ユーザーが「New Post」リンクをクリックする → newアクションが実行される
  2. newアクションでは @post = Post.new で空の投稿オブジェクトを作成
  3. new.html.erbform_with(model: @post) を使ってフォームを表示
  4. ユーザーがフォームに入力し、送信ボタンをクリック
  5. フォームのデータが createアクションに送信される
  6. createアクションでは @post = Post.new(post_params) でモデルオブジェクトを作成
  7. @post.save でデータベースに保存を試みる
  8. 保存に成功したら、redirect_to post_path(@post) で詳細ページにリダイレクト
  9. 保存に失敗したら、render :new でフォームを再表示

データを作成するという基本的な機能でも、さまざまな処理が行われていることがわかります。特に、redirect_torender の使い分けは重要ですので、しっかりと理解しておきましょう。

この流れはカリキュラムの中でも何度も出てくる内容ですので、しっかりと覚えておいてください。

まとめ

本章では、Railsにおけるnew, createアクションの役割と動作原理について学びました。

  • newアクションは、新規データ作成用の空のモデルオブジェクトを生成する
  • createアクションは、フォームから送信されたデータをもとに実際にデータベースに保存する
  • form_withは、フォームを簡単に作成するためのRails独自のヘルパーメソッド
  • redirect_torenderの違いは、アクションを再実行するかしないかの違い
  • 保存失敗時にrenderを使うことで、入力内容を保持したままフォームを再表示できる

これらの概念を理解することで、ユーザーからの入力を受け取り、データベースに保存するという基本的な処理の流れがわかります。次の章では、edit, updateアクションについて学んでいきましょう。

Previous
Rails の index, show アクションを理解しよう