Rails ブックマークの実装part1
実装内容
- 掲示板の☆ボタンを押すと、その掲示板をブックマーク/解除出来る機能を作成。
- ブックマークすると★解除すると☆になるようにし、フラッシュメッセージも表示させる。
- 同じユーザが同じ掲示板を複数回ブックマークできないようにする。
- ブックマークの一覧ページを作成する。
実装の流れ
- 中間モデルの定義
- ルーティングを作成
- bookmarkコントローラの実装
- viewの実装
中間モデルの定義
userモデルとboardモデルの間にbookmarkモデルを作成する。
userモデル
id | user |
---|---|
1 | meo |
bookmarkモデル
←中間モデル
id | user | board |
---|---|---|
1 | meo | abcd |
boardモデル
id | board |
---|---|
1 | abcd |
user board bookmarkの関係
userが掲示板をブックマークするとその数だけ掲示板はブックマークを持っている。
つまり、userとboardはbookmarkを持っているので多対多
の関係であると言える。
中間モデルについて
多対多
である中間モデル(bookmark)には、外部キー制約(foreign_key)を付ける必要がある。
どのuserがどのboardをブックマークしたかuser_id
,board_id
が保存される。
bookmarkモデル
id | user | board |
---|---|---|
1 | user_id | board_id |
bookmarkモデルを作成
外部キーをつけたいのでreference型を使い作成する。
紐づくモデル名+「_id」で外部キーと呼ぶ。
今回はuser,boardと紐付けるので以下のコマンドを実行する。
$ rails g model Bookmark user:references board:references Running via Spring preloader in process 5265 invoke active_record create db/migrate/20210603064703_create_bookmarks.rb create app/models/bookmark.rb
同じユーザが同じ掲示板を重複してブックマークできないようにしたいので、
作成されたマイグレーションファイルへ以下の記述を追加する。
t.index [:user_id, :board_id], unique: true
unique:true
でテーブル内で重複するデータを禁止する一意性制約
をつける。
マイグレーションを実行。
$ rails db:migrate == 20210603064703 CreateBookmarks: migrating ================================== -- create_table(:bookmarks)
各モデルのアソシエーション
belongs_to :user belongs_to :board validates :user_id, uniqueness: { scope: :board_id }
データベース側に一意性制約
をつけたが、モデル側にも同様に記述する。
uniqueness
でオブジェクトが保存される前に属性の値が重複してないことを検証する。
has_many :boards, dependent: :destroy has_many :bookmarks, dependent: :destroy has_many :bookmark_boards, through: :bookmarks, source: :board
親(user)を削除したら、一緒に子(booksmark)も削除されるように
dependent: :destroy
を追加。
has_many :bookmark_boards, through: :bookmarks, source: :board
について
boards
は既にあるのでbookmark_boards
を定義し、ユーザがブックマークしている掲示板を取得する。
through: :bookmarks, source: :board
でbookmarksを通してboardモデルから掲示板を取得している。
has_many :bookmarks, dependent: :destroy belongs_to :user同じく、親(board)を削除したら、一緒に子(bookmarks)も削除されるように
dependent: :destroy
を追加。
userモデルにbookmarkしたか判定するメソッドを追加
コントローラにも記述できるが、モデルに書くのが一般的。
#ブックマーク追加 def bookmark(board) bookmark_boards << board end
<<
で引数で渡したboardレコードが先程has_many through
で定義したbookmark_boards
に保存される。
#ブックマーク外す def unbookmark(board) bookmark_boards.destroy(board) end
bookmark_boards
から引数のboardレコードを削除する。
#ブックマークしてるか判定するメソッド def bookmark?(board) bookmark_boards.include?(board) end
includes?
メソッドで引数のboardレコードが含まれてたらtrue、なければfalseを返す。part2へ
バグ修正メモ
System Versions
以下のエラーを修正していく。
blog一覧ページにアクセスした場合 blogの一覧ページが表示されること
ルーティングが間違っていた。
blogではblogsコントローラのindexアクションを呼び出せないのでエラーになっている
一覧画面を表示させるルーティングを追加
Rails.application.routes.draw do root to: 'blogs#index' resources :blogs do resources :comments, only: [:create, :destroy] end end
blog→blogsへ変更することで blog#index→blogs#indexになるのでindexが呼び出せる。
新規作成ページにアクセスした場合 blogの新規作成ページが正しく表示されていること
これはすぐに分かった。missing templateなので
_new.html.erb
→new.html.erb
フォルダ名が違った。
Blogs blog新規作成ページにアクセスした場合 blogの新規作成ができること
これはbinding.irb
で処理を確認した。
19: def create 20: @blog = Blog.new(blog_params) => 21: binding.irb 22: if @blog.save 23: redirect_to @blog, notice: 'Blog was successfully created.' 24: else 25: render :new 26: end #入力内容がサーバーへ送られてるか確認 irb(#<BlogsController:0x00007fd818aa88e8>):001:0> params => <ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"o7JUcI/7WunHKid9VYaN74qxOioy57jTpGlH954E5kUnM4IQS/8C1gf9YaEHO4WJ54EOO4StgWLdyZb2d/gBeA==", "blog"=><ActionController::Parameters {"title"=>"ssssss", "content"=>"ssssssss"} permitted: false>, "commit"=>"Create Blog", "controller"=>"blogs", "action"=>"create"} permitted: false> #@blogが作れてるか確認するとcontentがnilになってる irb(#<BlogsController:0x00007fd818aa88e8>):002:0> @blog => #<Blog id: nil, title: "ssssss", content: nil, created_at: nil, updated_at: nil> irb(#<BlogsController:0x00007fd818aa88e8>):003:0> @blog = Blog.new(blog_params) Unpermitted parameter: :content => #<Blog id: nil, title: "ssssss", content: nil, created_at: nil, updated_at: nil>
Unpermitted parameter: :contentからストロングパラメータにcontentが抜けていることがわかる
blog一覧から詳細ページにアクセスした場合 blogの詳細ページが表示されること
Did you mean? で言われてるように
comments→commentと直せばいいが,アソシエーションをかえたほうがいい
has_many :comment→has_many :commentsと直せばcommentsというメソッドが使えるようになる。
blog詳細ページで編集画面へのリンクをクリックした場合 blogの編集ページが表示されること
<%= link_to 'Edit', '/blogs/#{blog.id}/edit' %>
“”
が’’
になっていた。blog →@blogへ変更。
blog編集ページにアクセスした場合 blogの編集用フォームが表示されること
<% render 'form', blog: @blog %>
のイコールがぬけていたのでレンダーされていなかった。
@blogをローカル変数blogへ渡しフォームへ渡している
blog編集ページにアクセスした場合 内容を編集できる
編集ページにアクセスしたらブログの投稿フォームが表示されてしまう
サーバーログを確認するとcreateアクションが実行されている
_form.html.erb
をみてみるとform_with (model: Brog.new, local: true) do |f|
となっていて
引数に新規作成newを渡していることが原因でcreateアクションが実行されている。
blog: @blog
で定義したローカル変数blogにかえる。
blog新規作成ページにアクセスした場合 blogの新規作成でcontentも正しく作成できること
blogを新規作成した時にcontentだけ0になる。
データベースのテーブルの定義を確認すればいい。
マイグレーションファイルのcontentがinteger型で定義されていたので文字列を入れても0になっていた
rails db:rollbackで1つずつもどしてrails db:migrateすればおk
blog詳細ページでコメントした場合 blogの詳細ページにコメントが表示されること
@blog.comments.count.to_i
でintegerとして認識されてしまっている。
文字列として表示させたいのでto_s
にする。
動的タイトル表示の実装
ヘルパーを使ってタイトルを動的に表示させる。
ここでいうタイトルとはブラウザのタブに表示される文字のこと↓
実装内容
今回はヘルパーを使い、以下の各ページごとに動的にタイトルを表示させる。
content_forを使い、「ページごとのタイトル名 | タイトル名」のように表示させる。
トップページは「タイトル名」のみ表示。
実装手順
app/helpers/application_helper.rb
にメソッドを作成する。- 各ページのviewファイルへ設定を読み込ませる。
ヘルパーの作成。
empty?
メソッドを使い、真偽判定をする。
変数の値が""(文字列の場合)や値が空白の場合、真となる。
条件演算子(?:)
条件式 ? 真の時の値 : 偽の時の値
で記述。
page_titleの引数に''
を入れて例外が発生するのを防ぐ。
module ApplicationHelper def page_title(page_title = '') base_title = 'SAMPLE APP' page_title.empty? ? base_title : page_title + ' | ' + base_title end end
webサイト全体のviewへ適応する。
i18n翻訳ファイルの編集。
ja: defaults: login: 'ログイン' register: '登録' title: 'ユーザー登録' to_login_page: 'ログインページへ' user_sessions: new: Back: '戻る' title: 'ログイン' email: 'メールアドレス' password: 'パスワード' to_register_page: '登録ページへ' users: new: title: 'ユーザー登録' boards: title: '掲示板作成' index: '掲示板一覧'
ヘルパーで作成したメソッドを読み込む。
yield
メソッド
ビューを挿入すべき場所を指定。
〜 <title><%= page_title(yield(:title)) %></title> 〜
これでトップページにbase_titleが適応される。
各viewファイルに読み込ませていく
content_for
メソッド
コンテンツを名前付きのyieldとしてレンダリング
<%= content_for(:title, t('.title')) %> <%= content_for(:title, t('boards.title')) %> <%= content_for(:title, t('boards.index')) %>
掲示板詳細画面は閲覧中の掲示板名を表示させる。
<% content_for(:title, @board.title) %>
と記述。
Rails 掲示板にコメント機能を追加 part2
part1からの続き。
掲示板の詳細画面を実装したので、そこにコメント機能を実装していく。
投稿詳細画面へコメントの入力フォーム、エリアを追加する。
# Controllerへ追記。
boards_controllerにインスタンス変数@commentと@commentsを定義し、view側で呼び出す。
class BoardsController < ApplicationController skip_before_action :require_login, only: %i[index create show] def show if logged_in? @board = Board.find(params[:id]) @comment = Comment.new @comments = @board.comments.includes(:user).order(created_at: :desc) else redirect_back_or_to login_path, danger: 'ログインしてください' end end
logged_in?
メソッドを使い、ログインしていない場合 ログインページにリダイレクトされるようにした。
ここは単純にskip_before_action :require_login, only: %i[index create]
で良かった。
コメントを習得する際はN+1問題に注意し、includes
メソッドを使用しユーザーを呼び出す。
created_at: :desc
で作成された順に降順で表示。
コメントフォームのview
パーシャルで作成する。
ルーティングをネスト(board/board_id/comment)している場合はurlを2つ指定する。
<!-- コメントフォーム --> <div class="row mb-3"> <div class="col-lg-8 offset-lg-2"> <%= form_with model: comment, url: [board, comment], local: true do |f| %> <%= render 'shared/error_messages', object: f.object %> <%= f.label :body %> <%= f.text_area :body, class: 'form-control mb-3', rows: 4, placeholder: 'コメント' %> <%= f.submit class: 'btn btn-primary', value: '投稿' %> <% end %> </div> </div>
コメントエリアのview
こちらもパーシャルで作成。
コメントを作成したユーザーにだけアイコンを表示。
<!-- コメントエリア --> <%= comment.user.decorate.full_name %> <%= simple_format(comment.body) %> #入力値の改行に対応する 〜 <% if current_user.own?(comment) %> #自分のコメントだけ編集、削除アイコンを表示する <td class="action"> <ul class="list-inline justify-content-center" style="float: right;"> <li class="list-inline-item"> <a href="#" class='js-edit-comment-button' data-comment-id="<%= comment.id %>"> <%= icon 'fa', 'pen' %> </a> </li> <li class="list-inline-item"> <a href="#" class='js-delete-comment-button' data-comment-id="<%= comment.id %>"> <%= icon 'fas', 'trash' %> </a> </li> </ul> </td> <% end %> 〜
- コメントエリア
_comment.html.erb
にbootstrapを適応。
render comments
でよしなに_comment.html.erb
を呼び出している。
render @commnets
でも同様。
<div class="row"> <div class="col-lg-8 offset-lg-2"> <table id="js-table-comment" class="table"> <%= render comments %> </table> </div> </div>
掲示板一覧画面と掲示板詳細画面において、コメントを作成したユーザーにのみ編集、削除ボタンを表示させる。
- モデル側にユーザーのコメントであるか判定するメソッドを定義。
def own?(object) id == object.user_id end
<ul class='crud-menu-btn list-inline float-right'> <li class="list-inline-item"> <%= link_to '#', id: "button-edit-#{board.id}" do %> <%= icon 'fa', 'pen' %> <% end %> </li> <li class="list-inline-item"> <%= link_to '#', id: "button-delete-#{board.id}" do %> <%= icon 'fas', 'trash' %> <% end %> </li> </ul>
掲示板詳細画面show.html.erb
で
- 編集削除ボタン
- コメントフォーム
- コメントエリア
を読み込む。
フォームは、comments/_form.html.erb
をレンダーし、@boardと@commentを引数として渡している。
エリアは、comments /_comments.html.erb
をレンダーし、@commentsをcommentsという変数で使えるようにしている。
〜 <!-- 編集、削除ボタン--> <%= render 'crud_menus', board: @board %> <!-- コメントフォーム --> <%= render 'comments/form',{board: @board, comment: @comment} %> <!-- コメントエリア --> <%= render "comments/comments", {comments: @comments} %> 〜
- 掲示板一覧画面に編集、削除ボタンを読み込む。
<%= render 'crud_menus', board: board %>
comments_controllerの作成。
$ rails g controller comments
userとcommentでアソシエーションが定義されてるのでその点に注意する。
class CommentsController < ApplicationController def create comment = current_user.comments.build(comment_params) if comment.save redirect_to board_path(comment.board), success: 'コメントを作成しました' else redirect_to board_path(comment.board), danger: 'コメントを作成できませんでした' end end end
ログインユーザーに関連したコメントを表示させる。
current_user
メソッドでログイン中のユーザーを返す。
アソシエーションで定義されたオブジェクトを初期化する際はnewではなくbuildを使う。
comment = current_user.comments.build(comment_params)
ストロングパラメータの追加。
コメントをデータベースへ保存するにはuser_idとboard_idが必要でcommentの値(body)とboard_idが保存される。
(board_id: params[:board_id])
はURLであるboards/:board_id/comments
からboard_idをとってきている。
merge
はハッシュを合体させるメソッド。
private def comment_params params.require(:comment).permit(:body).merge(board_id: params[:board_id]) end
Rails 掲示板にコメント機能を追加 part1
概要
- 掲示板詳細画面を追加し、その中でコメントをできるようにする。
- 掲示板一覧画面から掲示板のタイトル名を押すと掲示板詳細画面へ行けるようにする。
- 書き込んだコメントが一番上に表示されるようにコメントの並び順を指定
- コメントした本人のみに削除・編集ボタンを表示させる
- コメントを投稿したユーザーが削除されると、ユーザーが投稿したコメントも削除される
- コメントが投稿された掲示板が削除されると、掲示板に投稿されたコメントも削除される
モデルの作成
Commentモデルを作成し、User、Boardモデルとアソシエーションを定義する。
Commentモデルに外部キー(user_id, board_id)を設定。
Commentモデルにバリデーションを設定。
今回はreferences
でモデルと同時に作成したのでCommentモデル側のアソシエーションは自動で定義される。
(CommentをUserとBoardに関連付けたいので、User、Boardにhas_many、Commentにbelong_toを定義)
$ rails g model Comment body:text user:references board:references invoke active_record create db/migrate/20210522131533_create_comments.rb create app/models/comment.rb
空のデータは登録できないようにNOTNULL制約を追加。
外部キーが記述されていることを確認する。
class CreateComments < ActiveRecord::Migration[5.2] def change create_table :comments do |t| t.text :body, null: false t.references :user, foreign_key: true t.references :board, foreign_key: true t.timestamps end end end
ここまで確認できたらrails db:migrate
を実行。
== 20210522131533 CreateComments: migrating =================================== -- create_table(:comments) -> 0.0036s == 20210522131533 CreateComments: migrated (0.0040s) ==========================
Commnetモデルにバリデーションの追加。
空の投稿を防ぐ為、presence: true
をつける。
class Comment < ApplicationRecord belongs_to :user belongs_to :board validates :body, presence: true, length: { maximum: 10_000 } end
User、Boardモデル側に以下のアソシエーションを定義
has_many :comments, dependent: :destroy
dependent: :destroyオプションを定義しないと、親モデル(user board)を削除した時に、その親モデルに紐づく子モデル(comment)の内容が残ってしまうので注意する。
掲示板詳細画面の作成
ルーティングの作成
今回は、BoardモデルとCommentモデルが紐付いてるので、ルーティングをネスト(入れ子)する必要がある。
ネストする場合はshallow
オプションを使用する。
shallow
オプションを使用するとルーティングが簡潔になる。
resources :boards, only: %i[index new create show] do resources :comments, only: %i[create destroy], shallow: true end
使用前
comments#createはcommentのidではなくboard_idを指定する。
Prefix Verb URI Pattern Controller#Action board_comments POST /boards/:board_id/comments(.:format) comments#create board_comment DELETE /boards/:board_id/comments/:id(.:format) comments#destroy
使用後
comments#destroyはcommentのidを指定するのでboard_idは指定しない。
Prefix Verb URI Pattern Controller#Action board_comments POST /boards/:board_id/comments(.:format) comments#create comment DELETE /comments/:id(.:format) comments#destroy
controllerの作成
@board = Board.find(params[:id])
でリクエストパラメータ(params)経由でidを受け取りBoardオブジェクトをデータベースから取得。
findはidによってそのモデルに対応するレコードをデータベースから検索する。
params[:id]にはURL “localhost:3000/boards/[id]”の[id]の部分が格納されてる。
class BoardsController < ApplicationController skip_before_action :require_login, only: %i[index create show] #... def show @board = Board.find(params[:id]) #...
viewの作成
boards/show.html.erb
を作成。
#... <%= image_tag @board.board_image.url, class: 'card-img-top img-fluid', size: '300x200' %> <%= @board.title %> #掲示板名の表示 <%= @board.user.decorate.full_name %> #user_decorator.rbのfull_nameメソッドを呼び出し、ユーザー名を表示する。 <%= l @board.created_at, format: :long %> #...
l
メソッドについて
l
メソッドはi18nにおいて日付や時刻を表す際に使用する。
config/locales/ja.ymlに書かれた内容を読み込む。
format
オプションを使うことで複数の書式を使い分けることができる。
ja: time: formats: default: "%Y年%m月%d日(%a) %H時%M分%S秒 %z" # 2021年05月23日(日) 02時10分39秒 +0900 long: "%Y/%m/%d %H:%M" # 2021/05/23 02:10 short: "%m/%d %H:%M" # 05/23 02:10
掲示板一覧画面から掲示板のタイトル名を押すと掲示板詳細画面へ行けるようにする。
掲示板一覧画面に詳細画面へのリンクを追加。
今回のboard_pathはidが指定されているのでlink_to prefix_path(渡すid)
と記述。
<%= link_to board.title, board_path(board.id) %>
part2へ続く。
掲示板に画像アップロード機能を実装
実装内容
- 掲示板作成画面から画像を投稿する。
- gem CarrierWaveとMiniMagickを利用。
- サムネイルというラベルを追加し、画像の選択をする。
- 画像を選択せずに投稿した場合、デフォルトの画像を表示。
- アップロードできるファイルはjpg, jpeg, png, gifのみに制限。
- 画像投稿時にプレビューとして表示。
導入
CarrierWaveはRailsアプリケーションに画像アップロード機能をつけることができる。
MiniMagickはアップロードされた画像のリサイズなどができる。
MiniMagickを使用するには、ImageMagickが必要なので事前にインストールする。
$ brew install imagemagick
gem 'carrierwave' gem 'mini_magick'
Boardモデルのboardsテーブルにboard_imageカラムを追加する。
board_imageカラムには画像情報(ファイルそのものではない)が格納される。
$ rails g migration AddBoardImageToBoards board_image:string Running via Spring preloader in process 10390 invoke active_record create db/migrate/20210520113137_add_board_image_to_boards.rb
画像を選択せずに掲示板を作成できるようにしたいので、マイグレーションファイルはそのままでNOTNULL制約はつけない
class AddBoardImageToBoards < ActiveRecord::Migration[5.2] def change add_column :boards, :board_image, :string end end
$ rails db:migrate == 20210520113137 AddBoardImageToBoards: migrating ============================ -- add_column(:boards, :board_image, :string) -> 0.0013s == 20210520113137 AddBoardImageToBoards: migrated (0.0016s) ===================
画像uploaderの作成
ジェネレータでuploaderを生成することから始める。
$ rails g uploader image(アップローダー名)
これにより次のファイルが生成される。
ここには画像のリサイズなど、設定を記述する。
create app/uploaders/image_uploader.rb
uploaderとモデルを紐付ける。
今回はBoardモデルと紐付ける。
class Board < ApplicationRecord mount_uploader :board_image, ImageUploader
image_uploader.rbの編集
このファイルはデフォルトでコードが記述されており、コメントアウトを解除して使用する。 MiniMagickでリサイズするので記述。
class ImageUploader < CarrierWave::Uploader::Base include CarrierWave::MiniMagick
デフォルトの設定
storage :file
public/uploads/
に画像が保存される。def store_dir
画像が保存される先のpathを指定。
storage :file def store_dir "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" end
追加設定
def default_url 'board_placeholder.png' # app/assets/images/board_placeholder.png end def extension_allowlist %w(jpg jpeg gif png) end
viewの作成
掲示板作成フォームを編集する。
画像投稿時にプレビューとして表示するには、画像はまだデータベースへ登録されていないので、選択した画像はjavascriptを使って表示させる必要がある。
#... <div class="form-group"> <%= f.label :board_image %> <%= f.file_field :board_image, onchange: 'previewFile(preview)', class: 'form-control mb-3', accept: 'image/' %> <%= f.hidden_field :board_image_cache %> </div> <div class='mt-3 mb-3'> <%= image_tag board.board_image.url, id: 'preview', size: '300x200' %> </div> #...
onchange
イベントハンドラでjavascriptを指定フォームの入力値、選択が変更されたときに処理を行う。
accept
でファイルを指定する。image/は画像ファイル、video/*は動画ファイル。
<%= f.hidden_field :board_image_cache %>
これは、掲示板作成できなかった場合、アップロードした画像を消えないようにする処理。
javascriptでプレビューを表示させる。
jsファイルの作成。
application.jsは個別のjavascriptを設定するファイルなので、ここには記述してはいけない。
function previewFileWithId(id) { const target = this.event.target; const file = target.files[0]; const reader = new FileReader(); reader.onloadend = function () { preview.src = reader.result; } if (file) { reader.readAsDataURL(file); } else { preview.src = ''; } }
掲示板一覧画面へアップロードした画像を表示させる。
board.rb
のboard_imageからurlをとってくる。
<%= image_tag board.board_image.url, class: 'card-img-top', size: '300x200' %>
ストロングパラメータに画像のフィールドを追加。
privatedef board_params params.require(:board).permit(:title, :body, :board_image, :board_image_cache) end end
ローカルでアップロードした画像をリモートへアップロードしないように設定。
.gitignoreに画像のアップロード先を追加する。
# Ignore vendor /public/uploads
rmコマンドでリモートリポジトリから削除する。
$ git rm -r --cached public/uploads
# public/uploadsディレクトリ下のファイルをすべて削除
ログイン中のユーザーで掲示板の新規作成をする
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>