ログイン中のユーザーで掲示板の新規作成をする
UserモデルとBoardモデルにアソシエーションを設定
Userと関連付いたboardを作成するので、has_manyをUserモデルに定義する。
class User < ApplicationRecord authenticates_with_sorcery! validates :password, length: { minimum: 3 }, if: -> { new_record? || changes[:crypted_password] } validates :password, confirmation: true, if: -> { new_record? || changes[:crypted_password] } #... has_many :boards, dependent: :destroy end
controller
UserモデルとBoardモデルにアソシエーションが定義されている場合の記述。
以下のように記述した場合、登録に成功しない。
class BoardsController < ApplicationController skip_before_action :require_login, only: %i[index create] def create @board = Board.new(board_params) ←ここ if @board.save redirect_back_or_to boards_path, info: '掲示板を作成しました' else flash.now[:danger] = '掲示板を作成できませんでした' render 'new' end end private def board_params params.require(:board).permit(:title, :body) end end
原因
user_idカラム作成時にNOT NULL制約をつけているので、上記コードだとuser_idがnilになってしまう。
ログインユーザーに関連した掲示板のオブジェクトを作成するには
@board = current_user.boards.build(board_params)
current_user.boards.newとすることで、user_idを登録したboardオブジェクトを初期化できる。引数としてパラメータ(board_params)を渡すことでフォームに入力されたオブジェクトを作成することができる。アソシエーションで定義されたオブジェクトを初期化する際はnewではなくbuildを使う。(どちらを記述しても挙動は変わらない)
また、以下のようにmergeを使ってuser_idを用意することもできるが、
Board.new(board_params.merge(user_id: current_user.id))
アソシエーション関連であるオブジェクトは強調したほうが良いので、モデルのアソシエーションに関連性のない情報を登録する時にmergeは使うと良い。
view
ビューはパーシャルを使い表示させる。
<%= form_with model:board, local: true do |f| %> <%= render 'shared/error_messages', object: f.object %> <div class="form-group"> <%= f.label :title %> <%= f.text_field :title, class: "form-control" %> </div> <div class="form-group"> <%=f.label :body %> <%=f.text_area :body, class: "form-control", low: "10", style: "height: 200px" %> </div> <%= f.submit "登録する", class: "btn btn-primary" %> <% end %> </div> </div> </div>
パーシャルをnew.htmlで読み込む。
<div class="container"> <div class="row"> <div class="col-lg-8 offset-lg-2"> <h1>掲示板作成</h1> <%= render 'form', { board: @board } %> </div> </div> </div>