Rails 入門まとめ
Rails 入門まとめ
基本コマンド
プロジェクト作成
rails new store
Rails サーバー起動
bin/rails s
モデル・レコード
モデル作成
bin/rails generate model Product name:string
- db/migrateにマイグレーションファイル作成
- app/models/product.rbというActive Recordモデル作成
- このモデルで使うテストファイルとフィクスチャを作成
マイグレーション実行
bin/rails db:migrate
直前のマイグレーションに戻す
bin/rails db:rollback
Railsコンソール実行(Railsと対話)
bin/rails console
レコード作成・保存
product = Product.new(name: "T-Shirt")
product.save
Product.create(name: "Cap") # 作成・保存
レコード検索
Product.all # 全件
Product.where(name: "Pants") # where
Product.order(name: :asc) # orderBy
Product.find(1) # id
レコード更新
product = Product.find(1)
product.update(name: "Shoes")
or
product = Product.find(1)
product.name = "Shoes"
product.save
レコード削除
product.destroy
バリデーション
# app/models/product.rb
class Product < ApplicationRecord
validates :name, presence: true # nameカラムの存在確認
end
バリデーションすることでレコード登録するときに値の検証が自動でできる
登録に失敗した時の確認は以下
product.errors
product.errors.full_messages # わかりやすいメッセージ
ルーティング
ルーティング定義
config/routes.rb
Rails.application.routes.draw do
# (省略)
# /products への GETメソッド は ProductController の indexメソッドへ
# ルーティング という意味
get "/products", to: "products#index" # この行を追加
end
主なコントローラアクション
- index: すべてのレコードを表示
- new: 新しいレコード1件を作成するためのフォームをレンダリング
- create: newのフォーム送信を処理し、エラーを処理してレコードを1件作成
- show: 指定のレコード1件をレンダリングして表示
- edit: 指定のレコード1件を更新するためのフォームをレンダリング
- update: editのフォーム送信を処理し、エラーを処理してレコードを1件更新
- destroy: 指定のレコード1件を削除
get "/products", to: "products#index"
get "/products/new", to: "products#new"
post "/products", to: "products#create"
get "/products/:id", to: "products#show"
get "/products/:id/edit", to: "products#edit"
patch "/products/:id", to: "products#update"
put "/products/:id", to: "products#update"
delete "/products/:id", to: "products#destroy"
resources :products # これだけあれば上記と同等
ルーティング確認コマンド
bin/rails routes
コントローラー作成
# ルーティングは作成したので --skip-routes
bin/rails generate controller Products index --skip-routes
作成されるコントローラーファイル
# app/controllers/products_controller.rb
class ProductsController < ApplicationController
# これがアクションになる
# 中身が空だが、その場合はデフォルトで同名のテンプレートをレンダリング
def index
end
end
indexを実行すると以下をレンダリング
# app/views/products/index.html.erb
<h1>Products#index</h1>
<p>Find me in app/views/products/index.html.erb</p>
ルートパスにルーティングさせる
root "products#index"
ビューにデータを渡す
class ProductsController < ApplicationController
def index
# ビューにデータを渡す時はインスタンス変数を使用
@products = Product.all
end
end
表示するビューを変更
<h1>Products</h1>
<div id="products">
<% @products.each do |product| %>
<div>
<%= product.name %>
</div>
<% end %>
</div>
showアクションの追加
class ProductsController < ApplicationController
def index
@products = Product.all
end
def show
# paramsハッシュから値を取得
@product = Product.find(params[:id])
end
end
showのビューを作成
# app/views/products/show.html.erb
<h1><%= @product.name %></h1>
<%= link_to "Back", products_path %>
routesが以下だった場合のヘルパーメソッド
Prefix | Verb | URI Pattern | Controller#Action |
---|---|---|---|
products | GET | /products(.:format) | products#index |
product | GET | /products/:id(.:format) | products#show |
- products_path: "/products"`というパスを生成
- products_url: "http://localhost:3000/products"`というURLを生成
- product_path(1): "/products/1"というパスを生成
- product_url(1): "http://localhost:3000/products/1"というURLを生成
link_toを使用したindex.html.erb
<h1>Products</h1>
<div id="products">
<% @products.each do |product| %>
<div>
<%= link_to product.name, product %>
</div>
<% end %>
</div>
newアクションの追加
# Productコントローラー
def new
@product = Product.new
end
indexのビュー更新
<h1>Products</h1>
<%= link_to "New product", new_product_path %>
<div id="products">
<% @products.each do |product| %>
<div>
<%= link_to product.name, product %>
</div>
<% end %>
</div>
newのビュー追加
<h1>New product</h1>
<%= form_with model: @product do |form| %>
<div>
<%= form.label :name %>
<%= form.text_field :name %>
</div>
<div>
<%= form.submit %>
</div>
<% end %>
createアクションの追加
def create
@product = Product.new(product_params)
if @product.save
redirect_to @product
else
render :new, status: :unprocessable_entity
end
end
private
def product_params
params.expect(product: [ :name ])
end
product_paramsは渡されたパラメータを検査する
productに許可しているのは:nameだけなので、その他の値は無視される
詳しくはStrongParameter参照
redirect_toに Active Recordオブジェクトを渡すと、そのレコードのshowアクションへのパスを生成する
saveが失敗して、レコードが有効にならなかった場合、同じフォームを再レンダリングして、ユーザーが無効なデータを修正できるようにする
createアクションのelseではrender :newをレンダリングするように指示している
編集用にedit, updateを追加
def edit
@product = Product.find(params[:id])
end
def update
@product = Product.find(params[:id])
if @product.update(product_params)
redirect_to @product
else
render :edit, status: :unprocessable_entity
end
end
showのビューにeditを追加
# app/views/products/show.html.erb
<h1><%= @product.name %></h1>
<%= link_to "Back", products_path %>
<%= link_to "Edit", edit_product_path(@product) %>
before_actionでcontrollerをDRY
class ProductsController < ApplicationController
before_action :set_product, only: %i[ show edit update ]
def index
@products = Product.all
end
def show
end
def new
@product = Product.new
end
def create
@product = Product.new(product_params)
if @product.save
redirect_to @product
else
render :new, status: :unprocessable_entity
end
end
def edit
end
def update
if @product.update(product_params)
redirect_to @product
else
render :edit, status: :unprocessable_entity
end
end
private
def set_product
@product = Product.find(params[:id])
end
def product_params
params.expect(product: [ :name ])
end
end
before_actionを使うと、アクション間で共有されているコードを抽出して、アクションの直前に実行できる
ビューを切り出す(パーシャル)
newとeditのビューが似ているので、共通部分として切り出す
パーシャルはファイル名の頭にアンダースコアをつける
# app/views/products/_form.html.erb
<%= form_with model: product do |form| %>
<div>
<%= form.label :name %>
<%= form.text_field :name %>
</div>
<div>
<%= form.submit %>
</div>
<% end %>
new, editのビューはパーシャルを活用し、以下の様になる
<h1>New product</h1>
<%= render "form", product: @product %>
<%= link_to "Cancel", products_path %>
<h1>Edit product</h1>
<%= render "form", product: @product %>
<%= link_to "Cancel", @product %>
destroyアクションを追加
before_action :set_product, only: %i[ show edit update destroy ]
...
def destroy
@product.destroy
redirect_to products_path
end
destroyビュー
<h1><%= @product.name %></h1>
<%= link_to "Back", products_path %>
<%= link_to "Edit", edit_product_path(@product) %>
<%= button_to "Delete", @product, method: :delete, data: { turbo_confirm: "Are you sure?" } %>
button_toは適切にアクションをトリガーしてくれる
turbo_confirmデータ属性は、フォームを送信する前にユーザーに確認ダイアログを表示するようにTurboというJavaScriptライブラリに指示する
認証機能を追加する
bin/rails generate authentication
続けてマイグレーションを行うとUserモデル用のusersテーブルと、Sessionモデル用のsessionsテーブルをデータベースに追加する
bin/rails db:migrate
コンソールでユーザー作成
store(dev)> User.create! email_address: "you@example.org", password: "s3cr3t", password_confirmation: "s3cr3t"
ログアウト機能追加
<!DOCTYPE html>
<html>
<!-- (省略) -->
<body>
<nav>
<%= link_to "Home", root_path %>
<%= button_to "Log out", session_path, method: :delete if authenticated? %>
</nav>
<main>
<%= yield %>
</main>
</body>
</html>
ユーザー認証されているときのみログアウトボタン表示
認証なしの時のアクセスを許可する
class ProductsController < ApplicationController
allow_unauthenticated_access only: %i[ index show ]
# (省略)
end
ユーザー認証されているときだけリンクを表示
# app/views/products/index.html.erb
<%= link_to "New product", new_product_path if authenticated? %>
ユーザー認証されていないときはログインリンク表示
<%= link_to "Login", new_session_path unless authenticated? %>
ユーザー認証されているときだけedit, destroyリンク
# app/views/products/show.html.erb
<h1><%= @product.name %></h1>
<%= link_to "Back", products_path %>
<% if authenticated? %>
<%= link_to "Edit", edit_product_path(@product) %>
<%= button_to "Destroy", @product, method: :delete, data: { turbo_confirm: "Are you sure?" } %>
<% end %>
キャッシュ機能を追加する
工事中
Discussion