Help us understand the problem. What is going on with this article?

Railsでリソースを入れ子にする

More than 5 years have passed since last update.

意外に大変だったので共有する。なお、開発環境にはRubyMineを使うが、その他の場合もほぼ同じだろう。

方針

Userリソースの下にWeightリソースを入れ子にして入れる(レコーディング・ダイエット・アプリケーション)。

Userモデルをつくる

  1. コードを書く

ユーザ認証に必要なbcrypt-ruby gemを入れる。

Gemfile
gem "bcrypt-ruby"
  1. bundle install

  2. Tools > Run Rails Generator > scaffold > User name:string password_digest:string

  3. Tools > Run Rake Task > db:migrate

  4. コードを書く

app/models/user.rb
class User < ActiveRecord::Base
  attr_accessible :name, :password_digest, :password, :password_confirmation
  validates :name, presence: true, uniqueness:true
  has_secure_password
end
  1. コードを書く
app/views/users/_form.html.erb
<%= form_for(@user) do |f| %>
  <% if @user.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>

      <ul>
      <% @user.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </div>

  <!--<div class="field">
    <%= f.label :password_digest %><br />
    <%= f.text_field :password_digest %>
  </div>-->

  <!-- added from here -->
  <div class="field">
    <%= f.label :password %><br />
    <%= f.text_field :password %>
  </div>

  <div class="field">
    <%= f.label :password_confirmation %><br />
    <%= f.text_field :password_confirmation %>
  </div>
  <!-- to here -->

    <div class="actions">
    <%= f.submit %>
  </div>
<% end %>
  1. http://localhost:3000/usersを見るとエラーメッセージが出ていることがある
undefined method `key?' for nil:NilClass

このときはサーバを再起動すればなおる

  1. http://localhost:3000/users/newを見るとユーザ登録画面が表示される

  1. ためしにユーザを登録してみると、ちゃんと登録されている

Weightモデルをつくる

  1. Tools > Run Rails Generator > scaffold > Weight user:references weight_record:decimal memo:text

  2. Tools > Run Rake Task > db:migrate

  3. コードを書く

app/models/users.rb
has_many :weights
  1. モデル図を見る

userが0..*のweightを持っていることがわかる。

  1. コードを書く
config/routes.rb
  resources :users do
    resources :weights
  end
  1. Tools > Run Rake Task > routes
    user_weights GET    /users/:user_id/weights(.:format)          weights#index
                 POST   /users/:user_id/weights(.:format)          weights#create
 new_user_weight GET    /users/:user_id/weights/new(.:format)      weights#new
edit_user_weight GET    /users/:user_id/weights/:id/edit(.:format) weights#edit
     user_weight GET    /users/:user_id/weights/:id(.:format)      weights#show
                 PUT    /users/:user_id/weights/:id(.:format)      weights#update
                 DELETE /users/:user_id/weights/:id(.:format)      weights#destroy
           users GET    /users(.:format)                           users#index
                 POST   /users(.:format)                           users#create
        new_user GET    /users/new(.:format)                       users#new
       edit_user GET    /users/:id/edit(.:format)                  users#edit
            user GET    /users/:id(.:format)                       users#show
                 PUT    /users/:id(.:format)                       users#update
                 DELETE /users/:id(.:format)                       users#destroy

UserのビューとWeightのコントローラ、ビューは、この新しいrouteに対応していないので、すべて直してあげる必要がある。

User indexビューをつくる

  1. コードを書く
app/views/users/index.html.erb
<h1>Listing users</h1>

<table>
  <tr>
    <th>Name</th>
    <th>Password digest</th>
    <!-- added next line -->
    <th></th>
    <th></th>
    <th></th>
    <th></th>
  </tr>

<% @users.each do |user| %>
  <tr>
    <td><%= user.name %></td>
    <td><%= user.password_digest %></td>
    <!-- added next line -->
    <td><%= link_to 'Show Weights', user_weights_path(user) %></td>
    <td><%= link_to 'Show', user %></td>
    <td><%= link_to 'Edit', edit_user_path(user) %></td>
    <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>
  </tr>
<% end %>
</table>

<br />

<%= link_to 'New User', new_user_path %>

これでユーザ一覧の画面を表示できる。

Weightコントローラをつくる

  1. コードを書く
app/controllers/weights_controller.rb
class WeightsController < ApplicationController
  # GET /weights
  # GET /weights.json
  def index
    #@weights = Weight.all
    @user = User.find(params[:user_id])
    @weights = @user.weights.all

    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @weights }
    end
  end

  # GET /weights/1
  # GET /weights/1.json
  def show
    #@weight = Weight.find(params[:id])
    @user = User.find(params[:user_id])
    @weight = @user.weights.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.json { render json: @weight }
    end
  end

  # GET /weights/new
  # GET /weights/new.json
  def new
    #@weight = Weight.new
    @user = User.find(params[:user_id])
    @weight = @user.weights.build

    respond_to do |format|
      format.html # new.html.erb
      format.json { render json: @weight }
    end
  end

  # GET /weights/1/edit
  def edit
    #@weight = Weight.find(params[:id])
    @user = User.find(params[:user_id])
    @weight = @user.weights.find(params[:id])
  end

  # POST /weights
  # POST /weights.json
  def create
    #@weight = Weight.new(params[:weight])
    @user = User.find(params[:user_id])
    @weight = @user.weights.build(params[:weight])

    respond_to do |format|
      if @weight.save
        #format.html { redirect_to @weight, notice: 'Weight was successfully created.' }
        format.html { redirect_to [@user, @weight], notice: 'Weight was successfully created.' }
        format.json { render json: @weight, status: :created, location: @weight }
      else
        format.html { render action: "new" }
        format.json { render json: @weight.errors, status: :unprocessable_entity }
      end
    end
  end

  # PUT /weights/1
  # PUT /weights/1.json
  def update
    #@weight = Weight.find(params[:id])
    @user = User.find(params[:user_id])
    @weight = @user.weights.build(params[:id])

    respond_to do |format|
      if @weight.update_attributes(params[:weight])
        #format.html { redirect_to @weight, notice: 'Weight was successfully updated.' }
        format.html { redirect_to [@user, @weight], notice: 'Weight was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: "edit" }
        format.json { render json: @weight.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /weights/1
  # DELETE /weights/1.json
  def destroy
    #@weight = Weight.find(params[:id])
    @user = User.find(params[:user_id])
    @weight = @user.weights.build(params[:id])
    @weight.destroy

    respond_to do |format|
      #format.html { redirect_to weights_url }
      format.html { redirect_to user_weights_url }
      format.json { head :no_content }
    end
  end
end

Weight indexビューをつくる

  1. コードを書く
app/views/weights/index.html.erb
<h1>Listing weights</h1>

<table>
  <tr>
    <th>User</th>
    <th>Weight record</th>
    <th>Memo</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>

<% @weights.each do |weight| %>
  <tr>
    <td><%= weight.user %></td>
    <td><%= weight.weight_record %></td>
    <td><%= weight.memo %></td>
    <!--<td><%#= link_to 'Show', weight %></td>-->
    <td><%= link_to 'Show', user_weight_path(weight.user, weight) %></td>
    <!--<td><%#= link_to 'Edit', edit_weight_path(weight) %></td>-->
    <td><%= link_to 'Edit', edit_user_weight_path(weight.user, weight) %></td>
    <!--<td><%#= link_to 'Destroy', weight, method: :delete, data: { confirm: 'Are you sure?' } %></td>-->
    <td><%= link_to 'Destroy', user_weight_path(weight.user, weight), method: :delete, data: { confirm: 'Are you sure?' } %></td>
  </tr>
<% end %>
</table>

<br />

<%#= link_to 'New Weight', new_weight_path %>
<%= link_to 'New Weight', new_user_weight_path %>

これで体重一覧の画面を表示することができる。

Weight newビューをつくる

  1. コードを書く
app/views/new.html.erb
<h1>New weight</h1>

<%= render 'form' %>

<%#= link_to 'Back', weights_path %>
<%= link_to 'Back', user_weights_path %>
app/views/_form.html.erb
<%#= form_for(@weight) do |f| %>
<%= form_for([@user, @weight]) do |f| %>
  <% if @weight.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@weight.errors.count, "error") %> prohibited this weight from being saved:</h2>

      <ul>
      <% @weight.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%#= f.label :user %><br />
    <%= f.label "User Id" %><br />
    <%#= f.text_field :user %>
    <%= f.text_field :user_id, disabled: true %>
  </div>
  <div class="field">
    <%= f.label :weight_record %><br />
    <%= f.text_field :weight_record %>
  </div>
  <div class="field">
    <%= f.label :memo %><br />
    <%= f.text_area :memo %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

これで新規体重の入力画面を表示できる。

Weight showビューをつくる

  1. コードを書く
app/views/show.html.erb
<p id="notice"><%= notice %></p>

<p>
  <!--<b>User:</b>-->
  <b>User Name:</b>
  <%#= @weight.user %>
  <%= @weight.user.name %>
</p>

<p>
  <b>Weight record:</b>
  <%= @weight.weight_record %>
</p>

<p>
  <b>Memo:</b>
  <%= @weight.memo %>
</p>


<%#= link_to 'Edit', edit_weight_path(@weight) %>
<%= link_to 'Edit', edit_user_weight_path(@user, @weight) %>
|
<%#= link_to 'Back', weights_path %>
<%= link_to 'Back', user_weights_path(@user) %>

これで新規体重を入力したあと確認画面を表示することができる。

Weight editビューをつくる

  1. コードを書く
app/views/weights/edit.html.erb
<h1>Editing weight</h1>

<%= render 'form' %>

<%#= link_to 'Show', @weight %>
<%= link_to 'Show', user_weight_path %>
|
<%#= link_to 'Back', weights_path %>
<%= link_to 'Back', user_weights_path %>

これで入力した体重を変更することができる。

おしまい。


ブログやってます:PAPA-tronix !

Feel-Physics
今はHoloLensのアプリを開発しており、技術記事はすべてブログ( http://weed.nagoya )に書いています。以前はSwift、OpenCV+Python、JavaScript、Objective-Cを書いていました。
http://feel-physics.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away