他ユーザーがCRUD処理できないようにする
少しだけ涼しくなってきた。朝の開発が心地よくなっている。今のコードでは自分以外のユーザーでも植物のCRUD処理ができてしまうので、それをなくしていこう。plant_controller.rb
へbefore action
を追加する
class PlantsController < ApplicationController
before_action :move_to_index, except: [:index, :show]
#----中略----
def move_to_index
# 自分以外のユーザーの植物の 編集/削除/新規作成はできない
if (user_signed_in?)
if (params[:user_id].to_i != current_user.id)
redirect_to action: :index
end
elsif
redirect_to action: :index
end
end
#----中略----
end
before_actionはこのコントローラー内のアクションがよびだされたときにはじめに実行される。今回はmove_to_index
というメソッドが実行されるように指定した。move_to_index
ではログインしていてかつログインIDと表示したページのuseridが一致しているときだけなにも起こらない。それ以外の場合はindexアクションが実行されるようになっている。
つまりは植物一覧ページへ飛ぶ。
温度湿度の表示
いよいよこんなかんじになってきた。所有している植物の温度と湿度を登録できるようにする。以前のDB設計の際に考えていたgrowth_record
を追加することにしよう。
rails g model growth_record
をターミナルで実行してmodelをつくる。
growth_reocerdテーブルへ各種カラムを書き込む。
class CreateGrowthRecords < ActiveRecord::Migration[6.0]
def change
create_table :growth_records do |t|
t.integer :plant_id, null: false
t.datetime :record_time
t.float :temp, null: false
t.float :humid, null: false
t.timestamps
end
end
end
temp(温度)とhumid(湿度)はfloat型で小数点を記録できるようにした。record_timeはまだ実装していないのでnullでも大丈夫なようにした。
次はrails g controller growth_reocords
でコントローラーを作成する。
class GrowthRecordsController < ApplicationController
def index
@user = User.find(params[:user_id])
@plants = @user.plants
@growth_record = @plants.growth_record
end
end
modelどうしの関連づけをする必要がある。
class GrowthRecord < ApplicationRecord
belongs_to :plant
end
class Plant < ApplicationRecord
belongs_to :user
has_many :growth_record
end
今回は植物のgrowth_recordのindexページへは飛ばない。植物詳細ページの内容がかわるだけである。だからroutes.rbの記述に変更はない。かわりに植物のコントローラーとviewファイルが変更される。
#---中略---
def show
@user = User.find(params[:user_id])
@plant = @user.plants.find(params[:id])
@growth_record = @plant.growth_record # ← 追加
end
#---中略---
<h3>植物の名前</h3>
<%= @plant.plant_name %><br>
<h3> 植物の写真 </h3>
<%= @plant.img %><br>
<%= @plant.img %><br>
<h3> 温度/湿度 </h3>
<!-- 追加↓-->
<% @growth_record.each do |g| %>
<li>日時:<%= g.record_time %> / 温度:<%= g.temp %> /湿度:<%= g.humid %> </li>
<% end %>
これでOK。ついでにseedデータも修正しておこう。
# ----中略-----
GrowthRecord.create(plant_id:1, record_time: "r22ose", temp: 25.2, humid:50.3)
GrowthRecord.create(plant_id:1, record_time: "r22ose", temp: 24.2, humid:70.3)
GrowthRecord.create(plant_id:1, record_time: "r22ose", temp: 21.2, humid:60.3)
GrowthRecord.create(plant_id:1, record_time: "r22ose", temp: 20.2, humid:30.3)
GrowthRecord.create(plant_id:2, record_time: "ro33se", temp: 25.6, humid:30.3)
よし。
温度湿度のCRUD処理
この記事もきりの良いところまで進めてしまおう。温度と湿度のCRUD処理ができるようする。まずはroutes.rbを修正しよう。
Rails.application.routes.draw do
root to: 'users#index'
devise_for :users
resources :users do
resources :plants do
resources :growth_records, shallow: true # 追加
end
end
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
shallow
というオプションがあることに気づいた。railsガイドの浅いネストにある。
Rails のルーティング - Railsガイド
この方法は、ルーティングの記述を複雑にせず、かつ深いネストを作らないという絶妙なバランスを保っています。:shallowオプションを使うことで、上と同じ内容をさらに簡単に記述できます。
これにより、show/edit/update/destroyアクションはネストの外にいくようになる。ターミナルでrails routesを打ちながら確認しよう。
user_plant_growth_records GET /users/:user_id/plants/:plant_id/growth_records(.:format) growth_records#index
POST /users/:user_id/plants/:plant_id/growth_records(.:format) growth_records#create
new_user_plant_growth_record GET /users/:user_id/plants/:plant_id/growth_records/new(.:format) growth_records#new
edit_growth_record GET /growth_records/:id/edit(.:format) growth_records#edit
growth_record GET /growth_records/:id(.:format) growth_records#show
PATCH /growth_records/:id(.:format) growth_records#update
PUT /growth_records/:id(.:format) growth_records#update
DELETE /growth_records/:id(.:format) growth_records#destroy
コントローラーやviewファイルは植物のCRUD処理の場合とあまり変わらず実装できた。よかった。
おまけ:「植物詳細」へ戻るリンクが難しかった
個人的に難しかったのがひとつあった。linkのurlである。今まではCRUD処理の後はマイページへ戻る仕様にしていたのでlinkの張り方は迷わなかった。以下のような形。
<%= link_to 'マイページへ', user_path(current_user.id) %>
でも、温度湿度のCRUD処理をした後はその植物の詳細ページへ戻るようにしたかった。いろいろ悩んだけど以下のようになった。
<%= link_to '詳細ページ', user_plant_path(current_user.id,@plant.id) %>
<%= link_to '詳細ページ', user_plant_path(current_user.id,@growth_record.plant_id) %>
pathの指定で()の中に複数の値を指定できることがわかった。
binding.pryでデバッグするのが便利
やっとおわった。ちょっと詰まったところはbinding.pryをつかって中の変数を観察するようにした。するとうまくいった。
単純にエラー文をググるだけでは全然うまくいかない。OKだったときとエラーができるときの差分を自分の中ではっきりと持つことが重要だった。そうして変数をいじったり場合を分けたりしてその差分を徐々に詰めていく…。
「この場合ではOKだったけどこうしたらNGだった」そんなイメージで。
一区切りついた。次はrspecのテストを試してみよう。