Rails4のScaffolding機能でCRUDアプリケーションの骨組みを作成する方法
[履歴] [最終更新] (2016/06/03 21:58:35)
最近の投稿
注目の記事

概要

Rails4には他のフレームワーク同様、CRUD (Create, Read, Update, Delete) を行うアプリケーションの骨組みとなるファイル一式を自動で出力するScaffolding機能があります。

事前準備

$ rails new myApp

としてアプリケーションを作成しておきます。そうではなく、既存のアプリケーションを利用する場合でかつ下記Scaffolding機能が自動生成するファイルが存在する場合、「HelloWorldを表示するまでに必要な設定および知識 (Rails4)」を参考にそれらをdestroyしておきましょう。

Scaffolding機能を利用した骨組みの自動生成

$ rails generate scaffold myModel field1:string field2:integer field3:date field4:boolean
(単数形とする。myModelsはダメです)

自動生成されるファイル一覧

  • モデル: app/models/my_model.rb
  • コントローラ: app/controllers/my_models_controller.rb
  • ビューテンプレート: app/views/my_models/index.html.erb
  • ビューテンプレート: app/views/my_models/edit.html.erb
  • ビューテンプレート: app/views/my_models/show.html.erb
  • ビューテンプレート: app/views/my_models/new.html.erb
  • ビューテンプレート: app/views/my_models/_form.html.erb
  • ビューテンプレート(JSON形式): app/views/my_models/index.json.jbuilder
  • ビューテンプレート(JSON形式): app/views/my_models/show.json.jbuilder
  • ビューヘルパー: app/helpers/my_models_helper.rb
  • JavaScript: app/assets/javascripts/my_models.js.coffee
  • CSS: app/assets/stylesheets/my_models.css.scss
  • CSS: app/assets/stylesheets/scaffolds.css.scss
  • DBマイグレーション: db/migrate/20140608073740_create_my_models.rb
  • ルーティングファイルへの追記: config/routes.rb
  • テストスクリプト: test/models/my_model_test.rb
  • テストスクリプト: test/fixtures/my_models.yml
  • テストスクリプト: test/controllers/my_models_controller_test.rb
  • テストスクリプト: test/helpers/my_models_helper_test.rb

DBマイグレーションを行っておきましょう。

$ rake db:migrate

サーバーを起動して

$ rails s

アクセスしてみましょう → http://localhost:3000/my_models

自動生成されたファイルのカスタマイズ

ルーティングファイル

config/routes.rb

MyApp::Application.routes.draw do
  resources :my_models
...

という記述が自動追記されたことによって

$ rake routes
       Prefix Verb   URI Pattern                   Controller#Action
    my_models GET    /my_models(.:format)          my_models#index
              POST   /my_models(.:format)          my_models#create
 new_my_model GET    /my_models/new(.:format)      my_models#new
edit_my_model GET    /my_models/:id/edit(.:format) my_models#edit
     my_model GET    /my_models/:id(.:format)      my_models#show
              PATCH  /my_models/:id(.:format)      my_models#update
              PUT    /my_models/:id(.:format)      my_models#update
              DELETE /my_models/:id(.:format)      my_models#destroy

資源myModelsを操作するために必要なルーティング一式が有効になっていることが分かります。"(.:format)" は省略時には ".html" が使用されます。上述の通りJSON形式のビューテンプレートも自動生成されているので、「http://localhost:3000/my_models.json」というアクセスも可能です。

ビューテンプレートファイル

'a'タグを出力するためのヘルパーで、例えば

<%= link_to 'リンク', 'http://example.com/', target: '_blank' %>
<%= link_to 'サイト内リンク', { controller: :my_models, action: :index }, id: :link %>

という記述で

<a target="_blank" href="http://example.com/">リンク</a>
<a href="/my_models" id="link">サイト内リンク</a>

というHTMLが生成されます。Scaffoldで生成されたファイルでもこのヘルパーは多用されており、例えば

app/views/my_models/index.html.erb

<%= link_to 'Show', my_model %>
<%= link_to 'Edit', edit_my_model_path(my_model) %>
<%= link_to 'Destroy', my_model, method: :delete, data: { confirm: 'Are you sure?' } %>
<%= link_to 'New My model', new_my_model_path %>

という記述があります。最初の例でURLを指定した箇所にモデルを指定すると、そのモデルのページへのリンクが生成されます。"edit_my_model_path" や "new_my_model_path" は、

$ rake routes

を実行して表示される一覧の "Prefix" に "_path" を付けたものです。また、"method:" や "data:" を指定することで

<a rel="nofollow" href="/my_models/1" data-method="delete" data-confirm="Are you sure?">Destroy</a>

といったHTMLが生成されます。"rake routes" における "Verb" がGET以外の場合には "method:" の指定が必要です。また、data-confirmによって確認ダイアログを表示できます。

部分テンプレートファイル

ビュー内で

<%= render 'form' %>

と記述すると、その部分に

app/views/my_models/_form.html.erb

...
<%= form_for(@my_model) do |f| %>
  ...

  <div class="field">
    <%= f.label :field1 %><br>
    <%= f.text_field :field1 %>
  </div>
  <div class="field">
    <%= f.label :field2 %><br>
    <%= f.number_field :field2 %>
  </div>
  <div class="field">
    <%= f.label :field3 %><br>
    <%= f.date_select :field3 %>
  </div>
  <div class="field">
    <%= f.label :field4 %><br>
    <%= f.check_box :field4 %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

を挿入することができます。新規登録画面と編集画面など、複数のビューで共通のものを一つにまとめる際に便利です。

コントローラファイル

before_action

複数のアクションメソッドに共通の処理は、一つのprivateメソッドにまとめておくと記述がシンプルになります。

app/controllers/my_models_controller.rb

class MyModelsController < ApplicationController
  before_action :set_my_model, only: [:show, :edit, :update, :destroy]
...
  private
    # Use callbacks to share common setup or constraints between actions.
    def set_my_model
      @my_model = MyModel.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def my_model_params
      params.require(:my_model).permit(:field1, :field2, :field3, :field4)
    end
end

":id" の意味については "$ rake routes" の出力を確認してみましょう。また、before_actionで直接はよび出されない処理であっても、例えばPOSTデータをハッシュ形式

{
  "field1" => 'somevalue'
  "field2" => 'somevalue'
  "field3" => 'somevalue'
  "field4" => 'somevalue'
}

で返す定型句を "my_model_params" にまとめておくことで、アクションメソッドを簡略化できます。

モデルを介したDBのテーブル更新

app/controllers/my_models_controller.rb

class MyModelsController < ApplicationController
  ...
  # POST /my_models
  # POST /my_models.json
  def create
    @my_model = MyModel.new(my_model_params)

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

  # PATCH/PUT /my_models/1
  # PATCH/PUT /my_models/1.json
  def update
    respond_to do |format|
      if @my_model.update(my_model_params)
        format.html { redirect_to @my_model, notice: 'My model was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: 'edit' }
        format.json { render json: @my_model.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /my_models/1
  # DELETE /my_models/1.json
  def destroy
    @my_model.destroy
    respond_to do |format|
      format.html { redirect_to my_models_url }
      format.json { head :no_content }
    end
  end
  ...
end
  • redirect_to → URLを指定してリダイレクト可能です。モデルを指定することもできます
  • notice: → ビューテンプレートで "<%= notice %>" として参照できます
  • render action: 'new' → app/views/my_models/new.html.erb をテンプレートとして使用 (リダイレクトではありません)
  • status: → HTTPステータスコード。レスポンスのヘッダに記載されます
  • location: → リソースURLで、モデルも指定可能。レスポンスのヘッダに記載されます
  • head :no_content → CONTENTのないHTTPレスポンスを返す
  • my_models_url → "$ rake routes" の出力における "Prefix" に "_url" を付けたものです

その他の関連情報

複数のレコードを同時に更新

where句で条件を指定

cnt = MyModel.where(field1: 'value').update_all(field1: 'value2') # シンボルで指定可能
# cnt: 更新した件数

order → limit

cnt = MyModel.order(:field1).limit(10).update_all('field2 = field2 * 0.5') # SQL形式でも指定可能
# cnt: 更新した件数

複数のレコードを同時に削除

where句で条件を指定

MyModel.where('field1 <> ?', 'value').destroy_all

destroy_all 内で指定しても同じ意味

MyModel.destroy_all(['field1 <> ?', 'value'])

トランザクション

トランザクションを使用すると、一連の処理がすべて成功しない場合、一部の実行済みの処理をロールバックして、何も実行してない状態まで戻すことができます。具体的には、コントローラ内で以下のように記述します。

def new
  MyModel.transaction do
    my_model_1 = MyModel.new({field1: 'value'})
    my_model_1.save! # 保存に失敗すると例外を返す (参考: .save は true/false を返す。トランザクションには不向き)
    raise 'わざと例外を発生させています'
    my_model_2 = MyModel.new({field1: 'value2'})
    my_model_2.save!
  end
    # 例外が発生しなかった場合の処理
  rescue => e
    # 例外が発生した場合の処理
    render :text => e.message
end

なお、トランザクションを使用するためには、データベース側が対応していることが必要です。MySQLの場合、ストレージエンジンとして有名なものに "MyISAM" と "InnoDB" があります。しかしながら "MyISAM" はトランザクションに未対応であるため、"InnoDB" 型としてテーブルを作成する必要があります。具体的には、マイグレーションファイル内で

class CreateMyModels < ActiveRecord::Migration
  def change
    create_table :my_models, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8' do |t|
      ...

    end
  end
end

と記述することでInnoDBを指定します。

コールバック

"DELETE" や "UPDATE" といったデータベース処理の直前や直後にコールバック関数を指定できます。具体的な用途としては、削除対象のレコードを履歴として保存する関数を作成しておきコールバック関数として指定するというものが考えられます。これによって、コントローラ内の各所で同じ処理を記述する必要がなくなり、コードを共通化できます。

class MyModel < ActiveRecord::Base
  after_destroy :callback_func

  private  #← プライベート関数として宣言しましょう
  def callback_func
    # 何か共通の処理
    logger.info('deleted: ' + self.inspect)
  end
end

ちなみに、destroyした "後" に "self" で内容を参照できているのは "DELETE" が発行される前に一度 "SELECT" が発行されているからです。Railsには "destroy" の他に "delete" メソッドがありますが、こちらの場合は "DELETE" だけが発行されるため、コールバック関数が登録できません。

MyModel.destroy(params[:id])  #→ SELECT → DELETE
MyModel.delete(params[:id])   #→ DELETEのみ
関連ページ
    概要 他のMVCフレームワークと同様に、Rails4にも様々なビューヘルパーが用意されています。そのうち、ここでは特にフォーム関連のタグを生成するためのヘルパーの使用方法についてまとめてみます。 事前準備 ビューヘルパーのサンプルを実行するために、 rails generate scaffold myModel field1:string field2:integer field3:da
    概要 クッキー、セッション、フラッシュ、リクエストヘッダー等 クッキー app/controllers/main_controller.rb cookies[:sample] = { value: 'val', expires: 2.months.from_now, domain: 'localhost', # 例えばlocalhost2のサーバからアクセスできない path
    概要 ユーザからの入力値チェックは、一般にクライアント側とサーバ側の両方で行います。クライアント側のチェックは、例えばブラウザのJavascriptによって行うことができます。これは悪意のないユーザの操作性向上のために行います。ここでは、悪意あるユーザがJavascriptを無効にして不正なデータを送信してきた場合などに備えサーバ側でチェックを行うためのRailsのバリデーション機能の使用方法を
    概要 Capistranoはサーバの遠隔操作を自動化する多目的なツールです。以下では特に Rails4 を Capistrano3 でデプロイする基本的な方法をまとめます。Rails の場合は Capistrano の設定が gem で提供されているため、Capistrano の知識がなくても基本的なデプロイはできます。独自にカスタマイズしたい場合など、本ページの内容を越えるものは