DBのリセット

[DBのリセット方法]


DBをリセットする主なコマンドは、以下の2つ!!

[rails db:reset] ([rails db:drop db:setup]と同義。)
今までのデータベースを初期化して、[db/seeds.rb]ファイルを使用して初期データを作成。

[db/schema.rb]ファイルを元にデータベースを作成。(マイグレーションファイルに変更を加えてもその変更内容が反映されない。)


[rails db:migrate:reset]
今までのデータベースを削除した後に、マイグレーションファイルを古い順から実行する。


両者の使い分け
マイグレーションファイルの内容を変更せずに、データベースを削除し[db/seeds.rb]ファイルに記載の初期データを投入したい場合は、[rails db:reset]

マイグレーションファイルの内容を変更して、データベースを削除したい場合は、[rails db:migrate:reset]


※注意
誤った内容で記載してしまったマイグレーションファイルを実行してしまった場合は、[rails db:rollback]でロールバックし内容を訂正する。(実行してしまったマイグレーションファイルをそのまま訂正して、[rails db:migrate]してもrails は、1度実行したマイグレーションファイルだと認識する為。)
共同開発などしている時に実行したマイグレーションファイルを訂正する時は、ロールバックせずに新しい内容でマイグレーションファイルを作成し、[rails db:migrate]する。(ロールバックなどすると他の開発者に影響を与えてしまう可能性がある為。)

テーブル同士を関連付ける設定

[テーブル同士を関連付けさせる方法]

既存のそれぞれのテーブルに以下のようにマイグレーションファイルを記述し、データベース上で紐付けをする。 (1つのUserに対して複数のBoard[掲示板]を持つように[1:多]の関係になるように、BoardにUserを示す外部キー[user_id]を与える。)

[db/migrateファイル]

def up
    execute 'DELETE FROM boards;'                
#executeでクエリが実行される。(既存のboardレコードが存在すると紐付くuserが決められず、[user_id]にNOT NULL制約をしていた場合、設定していたNOT NULL制約に引っかかってしまう為、既存のBoardsテーブルを全て削除)
    add_reference :boards, :user, null: false,  foreign_key: true        
#既存のテーブルに外部キーを追加する。(foreign_keyは、外部キー制約)
end

def down
    remove_reference :boards, :user, foreign_key: true 
end

別な書き方で以下のように記述することもできる。(テーブル作成時に外部キーと外部キー制約を付与)

[db/migrate]

def change
    create_table :boards do |t|
      t.string :title,     null:false
      t.text :body,        null:false
      t.references :user, :null: false, :foreign_key: true
      t.timestamps
    end
end

※注意
・外部キー制約を付ける場合は、自動でインデックスが付与される。
・以下のようにreference型を使わない場合は、[foreign_key: true]では外部キー制約にならない。

[db/migrateファイル]

def change
    create_table :boards do |t|
      t.string :title,     null:false
      t.text :body,        null:false
      t.integer :user_id
      t.timestamps
    end
     add_foreign_key :boards, :users    # add_foreign_key :対象のテーブル名, :指定先のテーブル
end

[外部キー制約とは?]

1.存在しない値を外部キーとして登録できない。 2.子テーブルの外部キーに値が登録されている場合、親テーブルのレコードは削除できない。 上記の2の条件を破る方法は、下記のようにモデルに[dependent: :destroy]オプションを指定する。

[app/models]

has_many :boards, dependent: :destroy       #親モデルが削除された際に、子モデルも一緒に削除する

[関連したモデルのインスタンスを取得する設定方法] 以下のように[1:多]の関係にあるモデルに以下のように記述する。

[app/models]

has_many :boards, dependent: :destroy     #関連付けさせる先のモデル名を複数形にする。
[app/models]

belongs_to :user    #関連付けさせる先のモデル名を単数形にする。

※注意
[has_many]や[belongs_to]は、[validates]より上に記載した方が良い。(rubocop規約のため。)

参考記事:

Railsの外部キー制約とreference型について - Qiita

日時の国際化

日時の国際化の手順

以下のように設定を記述する

[config/application.rb]

config.time_zone = 'Asia/Tokyo'       #タイムゾーンの設定
config.active_record.default_timezone = :local       
 #データベースに登録、参照する時の設定(タイムゾーンと同じにする時は、:localにする。)

※基本的には、タイムゾーンとDBに登録する時の設定は統一したほうが良い。

以下のようにアプリケーションのロケール設定をする

[config/application.rb]

config.i18n.default_locale = :ja



[config/locales/ja.yml]というファイルを作成し、表示形式を以下のように記述する。

[config/locales/ja.yml]

ja:
  time:
    formats:
      default: '%Y/%m/%d %H:%M'



lメソッドを使って、以下のように記述すると設定した表示形式で表示できる

[viewファイル]

<%= l board.created_at %>

参考記事:

【初心者向け・動画付き】Railsで日時をフォーマットするときはstrftimeよりも、lメソッドを使おう - Qiita

ActiveRecordのdefault_timezoneについて - Qiita

Railsでタイムゾーンを扱う方法を現役エンジニアが解説【初心者向け】 | TechAcademyマガジン

N+1 問題

[N+1 問題]とは?
必要以上にSQLが実行されてしまい、パフォーマンスが落ちること。
ex)掲示板の一覧画面に個別の掲示板を全て表示する場合(以下コードを参照) 個別の掲示板を取得するのにSQL1回実行 取得した個別の掲示板からユーザー情報を個別の掲示板の数だけN回取得するのにSQLN回実行する(取得した個別の掲示板モデルとユーザーモデルはアソシエーション関係にあり、個別の掲示板モデルに外部キーでuser_idがある)

[app/controller/board_controller.rb]

def index
  @boards = Board.all
end
[app/views/index.html.erb]

<% @boards.each do |board| %>
  <%= render partial: "board", locals: { board: board } %>
<% end %>

[N+1 問題の解消法]

controllerで全掲示板を取得する際に、includesメソッドを利用する。 includesメソッドは、アソシエーションの関連付けを事前に取得してN +1問題を解決するメソッドです。 [includesメソッドの使い方] モデル名.includes(:関連名)
※注意
関連名は、テーブル名の事ではない。

ex)

def index
    @boards = Board.all.includes(:user).order(created_at: :desc)
  end

個別掲示板を掲示板一覧画面でeach文で回さないようにする。 ex)

<%= render @boards %>

上記のように、コントローラーに記載した全掲示板を取得する変数をrenderにそのまま渡すと、[_board.html.erb]を持ってきて掲示板の一覧を表示することができる。

参考記事:

【Rails】N+1問題って何?原因と対処法を徹底解説! | Pikawaka - ピカ1わかりやすいプログラミング用語サイト

まるで魔法だな(パーシャルをrenderし繰り返し処理をする) - Qiita

Faker Gemの使い方

[Faker]Gemとは?
ダミーデータを作成するGem

※注意
デフォルトの設定だと同じ内容のデータが作成される為、それを防ぐ為には、以下のようにオプションを追加する。

email = Faker::Internet.unique.email

[Faker]Gemの導入手順 Gemfileに以下のように記載。

gem "faker"



bundle install

[db/seeds.rb]を作成し、以下のように記載する。 ex)

7.times do
    email = Faker::Internet.unique.email
    password = '0112'
    password_confirmation = '0112'
    last_name = Faker::Name.last_name
    first_name = Faker::Name.first_name
    user = User.create!(
        email: email,
        password: password,
        password_confirmation: password_confirmation,
        last_name: last_name,
        first_name: first_name
    )
end



rails db:seed 上記コマンドで[db/seeds.rb]を実行

※作成したダミーデータは、 rails db:migrate:reset でリセットできる。 (レコードを全て削除し、マイグレーションを実行するコマンド)

参考記事:

https://qiita.com/tanutanu/items/4006bd868affa535adb0

デコレーターの導入

デコレーターの導入 [dacoratorとは?] model→decorator→viewのようにmodelとviewの間にdecoratorを挟むことで、modelの内容を装飾してviewに表示できる。 modelの属性を装飾してviewに表示する場合は、そのメソッドをmodel,helper,decoratorいづれかに記載することになるが、以下のように使い分ける。 [decoratorの導入手順] Gemfileに以下を記載

gem "draper"



bundle install

rails generate draper:install ※draperをインストールする

rails generate decorator モデル名 ex)rails generate decorator User ※[app/decorators]ディレクトリにdecoratorのファイルができる。 decoratorファイルに記載があるdelegate_allとは、モデルのメソッドをそのままdecoratorでも使えるようにする。

[app/decorators]ディレクトリのdecoratorのファイルに以下のように記載する。

class UserDecorator < Draper::Decorator
  delegate_all

  def full_name
    "#{object.last_name} #{object.first_name}"
  end
end



viewファイルでdecoratorのメソッドを使う時は、以下のようにする。

<%= current_user.decorate.full_name %>

※メソッドの前に[decorate.]を入れる。

参考記事:

https://jiro8899.hatenablog.com/entry/2020/02/16/235117

https://study-diary.hatenadiary.jp/entry/2020/08/04/132953

https://tech.mof-mof.co.jp/blog/draper/

https://note.com/kentarotawara/n/n0c1ff2060cf7

https://kurose.me/decorator-draper/

flashメッセージ

flashメッセージの設定方法

flashオブジェクトは、デフォルトでalertキーとnoticeキーが設定されているが、特定のキーを指定する事でBootstrapに用意されているスタイルのフラッシュを利用できるようになる。 [app/controllers/application_controller.rb]に以下の記載をする事で、デフォルトのalertキーとnoticeキー以外で、Bootstrapに用意されているスタイルのフラッシュを定義できる。 また、上記の設定を行うことでControllerのredirectをフラッシュメッセージを含めて1行で記載できる。 add_flash_typesを定義しなくても毎回flashを記載すればフラッシュメッセージの表示はできるが、なるべく記述量を減らすようにする。

class ApplicationController < ActionController::Base
  add_flash_types :success, :info, :warning, :danger
end

下記のようにflashメッセージには、lazy lookup記法を使って翻訳情報を利用した方が国際化にも適している。

def create
    @user = login(session_params[:email], session_params[:password])
    if @user
      redirect_back_or_to root_path, success: (t '.success')
    else
      flash.now[:danger] = (t '.fail')
      render :new
    end
  end

Bootstrapを適用したflashメッセージを表示するために、[views/shared/flash_message.html.erb]などのファイルを作り[views/layouts/application.html.erb]などからrenderするなどする。 [views/shared/flash_message.html.erb]に以下のような記載をして、Bootstrapを適用したflashメッセージを表示する。

<% flash.each do |message_type, message| %>
  <div class="alert alert-<%= message_type %>"><%= message %></div>
<% end %>

Bootstrapを利用したキーとflashメッセージの見た目

f:id:kosukerino:20210427204639p:plain

参考記事:

https://qiita.com/Yama-to/items/4d19a714d8bf5bfbabdd