ランダムなレコードを1件取得する

親モデルに紐付いた子モデルのテーブルのダミーを作る際、 親モデルの取得方法にハマってしまったので忘備録。

10.times do
  title = Faker::Restaurant.name   #gem fakerを使ってます
  body = Faker::Restaurant.description
  user = User.offset(rand(User.count)).first   # これ
  Board.create!(
    title: title,
    body: body,
    user: user
  )
end

BoardはUserに紐付いてるのでUserを取ってこないとBoradが作れない。 Userからランダムに1つ取ってきたい。

はじめ、 User.find(User.ids.sample)User.find(User.pluck(:id).sample) としていたが、Userテーブルから全てのidを取得してしまっていてSQLのパフォーマンスが悪い。 User.offset(rand(User.count)).first とすることでランダムなレコードを1つだけ取得できるのでコスパ良し。 挙動が同じでもSQLのパフォーマンスも考える必要があると感じた。

Gem 'draper'を使ってDecoratorを導入

Decoratorとは

ソフトウェアデザインパターンの1つで、既存のオブジェクトに新しい機能や振る舞いを動的に追加するもの

ModelやHelperでもよさそうだが

・Modelに何でもかんでも詰め込むのはよくない(FatModel対策)

・ModelにはDBにアクセスするような処理のみを記載することで肥大化を防ぐ

・HelperはModelから独立し直接関係していない描画ロジックを実装するのに用いる

・Decoratorは特定のモデルにがっつり関連した描画ロジックを実装するのに用いる

Decoratorというデザインパターンを導入することで、ビューファイルにロジックを記述しないといったことができ、ビューをすっきりさせる

Gem 'draper'をインストール

gem 'draper'

bundle install さらに

$ rails g draper:install

でインストール完了。

Decoratorの作成

$ rails g decorator モデル名

とすることで指定したモデルに対応したDecoratorを作成 中にメソッドを記述してViewで呼び出せる

class モデル名Decorator < Draper::Decorator
delegate_all
  
def メソッド名
end
オブジェクト.decorate.メソッド名

まとめ

役割分担させることでDRYを意識し、可読性やメンテナンス性を上げる。

i18nの導入

i18nを使って日本語に対応します

デフォルト言語を日本語に設定

module RunteqNormal
  class Application < Rails::Application
    config.i18n.default_locale = :ja #デフォルトのlocaleを日本語にする
    config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s] #複数のロケールファイルを読み込むためにpathを設定
  end

Gem 'i18n-rails'をインストール

gem 'i18n-rails'

でbundle install

config/locales下にymlファイルを作成

今回はviewを扱うview/ja.yml、modelを扱うactiverecord/ja.ymlを作成

ja.ymlに日本語を設定

#インデントで階層を変える
ja:
  users:
    new:
      title: ユーザー登録   #viewファイルで<%= t '.title' %>とすることで「ユーザー登録」を取り出せる
  user_sessions:
    new:
      title: ログイン
  helpers:
    submit:
      create: 登録
ja:
  activerecord:
    models:
      user: ユーザー   
    attributes:
      user:
        name: 名前   #viewファイルで<%= t('activerecord.attributes.user.name') %>とすることで「名前」を取り出せる
        email: メールアドレス
        password: パスワード
        password_confirmation: パスワード確認

form_with内の記述

form_withでmodelを指定しているときはrailsが「これはユーザー登録に関するフォームだな」と認識してくれる

<%= form_with model: @user, local: true do |f| %>
  <%= f.label :email %>   #「メールアドレス」を取り出す
  <%= f.text_field :email, class: 'form-control' %>   #「登録」を取り出す
  
  <%= f.submit class: 'btn btn-primary' %>
<% end %>

modelの指定がない場合は

<%= form_with url: login_path, local: true do |f| %>
  <%= f.label :email, User.human_attribute_name(:email) %>
  <%= f.text_field :email, class: 'form-control' %>
  
  <%= f.submit class: 'btn btn-primary' %>
<% end %>

のように書く必要がある

form_with

form_with

情報を送信するためのヘルパーメソッド

<%= form_with url: root_path do |f| %>
  <%= f.text_field :name %>
  <%= f.submit %>
<% end %>

のようにするとボタンをクリックしたときrootに設定したurlに遷移する。

<%= form_with model: @user do |f| %> #model: 保存したいテーブルのクラスのインスタンス
  <%= f.text_field :name %>
  <%= f.submit %>
<% end %>

だと入力した情報をデータベースに保存する。

また、form_withはデフォルトでAjax通信になるが

<%= form_with model: @user, local: true do |f| %>

とすることでAjax通信を無効にできる。

rails g コマンドで自動生成されるファイルを設定する

rails g コマンドではファイルを自動生成してくれるが、不要なファイルがある場合は設定で生成しないようにできる。

class Application < Rails::Application
  config.generators do |g|
    #ここに設定を記述
    #例 g.assets false
  end
end

のように設定できる。

データベース関連のコマンド

データベースの編集について調べたのでいくつか書いておきます。

データベースの中身を空にする

rails db:migrate:reset

ロールバック(最新のマイグレーションファイルをdown状態にする)

rails db:rollback

2つ以上前のマイグレーションファイルをロールバックする

rails db:rollbadk STEP=2

数字を変えれば任意の数ロールバックできる

追記
以下のschemaを直接編集するのはよろしくなさそう。
ちゃんと新しくマイグレーションファイルを作成して変更を行う。

ファイルを直接編集

schema.rbを直接編集し、

rails db:schema:load

を実行するとその通りのテーブルを作成する データベースは空になるので注意

redirect_toの引数のインスタンス変数は何を表してるか

redirect_to @○○ はどういう意味?

このコードは一体どこにredirectしているのか?

redirect_to @post

調べてみると色んなものが省略されてこのように表記されているらしい。

redirect_to "/posts/#{@post.id}"

が省略されて

redirect_to post_url(@post.id)

さらに省略されて最終的に

redirect_to @post

になる。 ルーティングでいうと

get "/posts/:id" => "posts#show" 

に繋がっている。