assign_attributesメソッドとは
Railsを学習していてassign_attributes
メソッドを使う必要があり、調べてみたので、忘れないよう議事録として残そうと思いました。
結論
assign_attributes
は、複数の属性の値を更新するためのメソッドです。「DBに保存したくないけど値を更新したい」時に使用します。
assign_attributesメソッド
assign_attributes
は複数の属性の値を更新するためのメソッドです。
似たようなメソッドにupdate
やsave
などがあります。
これらのメソッドとの違いはDB保存の有無です。
assign_attributes
値を更新してDBに保存はしない。
update
、save
値を更新してDBに保存する。
ransackを使い検索機能を実装する
Ransackとは
ransackとは検索機能を実装するためのgemです。
検索に必要な機能を簡単に実装することができます。
検索機能を実装する利点
一覧画面などでは表示している情報が多いため、必要な情報を探すのに時間がかかってしまいます。
そこで検索機能を実装することで、探す手間を省くことができます。
Ransackの実装の流れ
1. ransackのインストール
2. コントローラーの編集
3. 検索フォーム作成
4. 検索結果の表示
1. ransackのインストール
まずはransackを利用するために、Gemfileに以下の行を追加します。
gem 'ransack'
その後bundleコマンドを実行してransack gem をインストールします。
$ bundle install
2. コントローラーの編集
ransackをインストールすると、検索を行うためのransackメソッドが追加されます。
検索をするためにアクションを追加していきます。
def index @q = current_user.tasks.ransack(params[:q]) @tasks = @q.result(distinct: true) end
上記で使われているメソッドを解説していきます。
params[:q]
params[:q]
は、この後に作成するビューファイルから送られてくるパラメーターです。検索フォームで入力した値がこのオブジェクト(@q)に渡されています。
ransack
ransack
は、送られてきたパラメーターを元にテーブルからデータを検索するメソッドです。
whrereメソッドのransack版です。
result
result
は、ransackメソッドで取得したデータをActiveRecord_Relationのオブジェクトに変換するメソッドです。
whereは実行結果のオブジェクトを返さないので、取得したデータから特定カラムのデータを取得しようとするとエラーになります。
なので、検索結果を変換する必要があります。
distinct: true
distinct: true
は、検索結果が重複することを防いでくれます。
関連する子テーブルの情報を条件に絞り込んで、親テーブルの検索結果を表示するときには必須となります。
検索時に重複したデータを結果として表示するケースは稀なので、癖としてつけておくほうがいいです。
3. 検索フォーム作成
ransackの提供するヘルパーsearch_form_for
を使って検索フォームを追加します。
検索フォームは他ページでも使うため、パーシャル化しておくと便利です。
<%= search_form_for q, class: 'mb-3', url: url do |f| %> <div class="input-group mb-3"> <%= f.search_field :title_or_body_cont, class: 'form-control', placeholder: ("検索ワード") %> <div class="input-group-append"> <%= f.submit class: 'btn btn-primary' %> </div> </div> <% end %>
search_form_for
メソッドを解説していきます。
search_form_for
search_form_for
は、ransackで用意されているヘルパーです。
form_forやform_withのransack版です。
_cont
_cont
は「名称に〇〇を含む」という検索を実現するためのメソッドです。
LIKE検索を行い、検索ワードで入力された文字が含まれるレコードを取得します。
ransackを利用する際は、検索フィールドを一定のルールで命名する必要があります。
f.search_field
f.search_field
は、検索フォームの設定をするためのメソッドです。
検索条件の設定などを行います。
4. 検索結果の表示
検索機能を実装したいビューファイルで、作成したパーシャルテンプレート(検索フォーム)を呼び出します。
url: url
呼び出し側で指定したパスをローカル変数に渡すことで、汎用性の高いパーシャルが作れます。
<%= render 'search_form', q: @q, url: tasks_path %>
まとめ
- ransackは検索機能を簡単に実装できるgem。
- ransackで提供されているメソッドを使えば、検索条件を簡単に設定できる。
参考にしたサイト
【Rails】 ransackを使って検索機能がついたアプリを作ろう! | Pikawaka - ピカ1わかりやすいプログラミング用語サイト
ページネーション kaminari
kaminariとは?
Railsでページネーションを利用するためによく利用されているgemのことです。
ページネーションとは?
ページネーションとは、レコード件数が一定数を超えた場合に複数のページに分割して表示するようにすることです。
一覧画面ではほとんどの確率で実装されています。
下記の写真に使われているのがページネーションです。
なぜページネーションが必要なの?
トップページは、画像や文章、関連する情報などを大量に表示していることが多いため、その分ブラウザを表示し終わるまでに時間がかかってしまいます。そうなると、ユーザーにとって使い勝手が悪くなり、ストレスに感じてしまいます。
このような問題を避けるためによく利用される方法がページネーションです。
情報を分割して表示することで、ユーザーにとって使いやすいようにできます。
ページネーション実装の大まかな流れ
- kaminariのインストール
- コントローラーにページネーションのコードを書く
- ページネーションの表示
- 表示数の設定
- デザインの調整
kaminari gem のインストール
まずはkaminariを利用するために、Gemfileに以下の行を追加します。
gem 'kaminari', '1.1.1' # rails5系なのでバージョン 1.1.1を使用します。
その後bundleコマンドを実行してkaminari gem をインストールします。
$ bundle install
Gemfile.lockに以下の行が追加されます。
kaminari (1.1.1) activesupport (>= 4.1.0) kaminari-actionview (= 1.1.1) kaminari-activerecord (= 1.1.1) kaminari-core (= 1.1.1) kaminari-actionview (1.1.1) actionview kaminari-core (= 1.1.1) kaminari-activerecord (1.1.1) activerecord kaminari-core (= 1.1.1) kaminari-core (1.1.1)
ページネーション定義
これでkaminariが定義しているメソッドを使うことができます。
def index @objects = Object.page(params[:page]) end
ページネーション表示
ページネーションを実装させたいviewファイルに下記のコードを記述します。
<%= paginate @objects %>
kaminariの設定
表示件数の設定を行いたいので、設定ファイルを作成します。
$ rails g kaminari:config
上記のコマンドで、config/initializers配下に以下の設定ファイルが作成されます。
# frozen_string_literal: true Kaminari.configure do |config| # config.default_per_page = 25 # config.max_per_page = nil # config.window = 4 # config.outer_window = 0 # config.left = 0 # config.right = 0 # config.page_method_name = :page # config.param_name = :page # config.max_pages = nil # config.params_on_first_page = false end
config.default_per_page = 25
この部分が表示件数の設定なので25
の部分を書き換えると、表示件数を変更することができる。
デザインの調整
kaminariが提供するジェネレータで、テーマ(Thema)と呼ばれるデザインの種類を指定して、好みの形のパーシャルテンプレートを生成できます。
今回はbootstrap4というテーマのパーシャルプレートを生成してみます。
次のコマンドをコンソールで実行します。
$ rails g kaminari:views bootstrap4
実行すると下記のような出力がされ、app/views/kaminari配下にいくつかビューテンプレートファイルが追加されます。
% rails g kaminari:views bootstrap4 Running via Spring preloader in process 43697 downloading app/views/kaminari/_first_page.html.erb from kaminari_themes... create app/views/kaminari/_first_page.html.erb downloading app/views/kaminari/_gap.html.erb from kaminari_themes... create app/views/kaminari/_gap.html.erb downloading app/views/kaminari/_last_page.html.erb from kaminari_themes... create app/views/kaminari/_last_page.html.erb downloading app/views/kaminari/_next_page.html.erb from kaminari_themes... create app/views/kaminari/_next_page.html.erb downloading app/views/kaminari/_page.html.erb from kaminari_themes... create app/views/kaminari/_page.html.erb downloading app/views/kaminari/_paginator.html.erb from kaminari_themes... create app/views/kaminari/_paginator.html.erb downloading app/views/kaminari/_prev_page.html.erb from kaminari_themes... create app/views/kaminari/_prev_page.html.erb
サーバーを再起動して、http://locallhost:3000にアクセスします。
テンプレートファイルが適用され、ナビゲーション部分の見た目が変更されます。
今回のまとめ
- kaminariは、ページネーションを簡単に実装できるgem
- ページネーションは、複数ページに分けて掲載するコンテンツを表示させるようにしたもの
- ページネーションの見た目を変える場合は、コマンド実行してapp/viewsフォルダに追加されたkaminariを変更する必要がある
参考にしたサイト
【Rails】 kaminariの使い方をマスターしよう! | Pikawaka - ピカ1わかりやすいプログラミング用語サイト
Ajax jQuery
今回はコメントの投稿と削除をAjax化する際に、 jQueryを使って実装しました。
しかし、jQuery自体あまり理解していなかったのでまとめていきます。
jQueryとは
jQueryとは、Javascriptのプログラミングでよく行われる定型的な処理を書きやすくしてくれる、オープンソースのライブラリです。 特に、HTMLやCSSの書き換え、イベントの設定やDOM操作や、Ajaxを得意としています。
DOM操作とは
JavaScriptで行う処理は、大きく分けて「入力」「加工」「出力」の3種類があります。
このうちの「出力」の中で、タグに囲まれたテキストや属性を書き換えたり、HTMLに要素を追加、削除するといった、HTMLやCSSを書き換える処理のことを「DOM操作」いいます。
jQueryの流れ
1. イベントを設定したい要素を取得する
2. 取得した要素にイベントを設定する。
jQueryのプログラムは上記の順序で処理することがほとんどです。
1の処理では、$()メソッドとCSSのセレクタを使って簡単にできます。
2の処理では、jQueryのonメソッドを使用します。
1. イベントを設定したい要素を取得する
要素を取得するには$()
メソッドを用います。
このメソッドは取得した要素を「jQueryオブジェクト」というjQuery独自のオブジェクトに変換してくれます。
取得した要素に対して、jQueryが提供するメソッドを使用することができます。
2. 取得した要素にイベントを設定する。
取得した要素にイベントをつけるため、jQueryが提供してくれているメソッドを用います。
$("tr#comment-<%= @comment.id %>").remove()
上記のコードは、コメントを消してくれるコードになります。
jQueryのメソッドが下記のページにまとめてあります。
Ajax | jQuery API Documentation
要素にid属性を付与する理由
<tr id="comment-<%= comment.id %>">
上記のようなコードを見て、なぜid属性を付与する必要があるのか気になり調べました。
サーバー側が、DOM要素としてどの要素が選択されて、Ajax通信が発生したのか知ることができないからです。
しかし、サーバーは要素のidを知っています。(user.idやpost.idのこと)
そこで、 DOM要素側にあらかじめid属性を付与し、判別できるようにします。
付与するid属性はHTMLの仕様としてのid属性です。
一般的には、HTMLのid属性に、タスクのidを含む文字列を指定します。
Ajax
今回実装したい機能
Ajaxを用いてブックマークした際、ボタンの切り替えを非同期で行う。
調べたこと
Ajaxとは何か?
Ajax
とは、Asynchronous JavaScript + XML の略で、非同期通信と呼ばれる通信方法のことを指します。
Ajax
を使用すると画面遷移がなく、データの一部だけ更新することができるので、操作性が良くなります。
同期通信、非同期通信の違い
同期通信と非同期通信の違いは、リクエストとレスポンスのタイミングが違うことです。
同期通信
- リクエストからレスポンスの一往復が終わらないと、次のリクエストを出せない。
サーバーから受け取った情報をもとにページ全体を1から表示するので時間がかかる。
非同期通信- レスポンス待ちでもページ全体の処理が止まることなく、別の操作が可能。
非同期通信の本質は、通信のリクエストとレスポンスのタイミングをずらせるので、
他の処理を並行して行えることです。
非同期通信を読み込みなしでページの一部だけ更新できる事と誤解している人が多いですが、これはAjax
の仕組みになります。
Ajaxの実装方法
- フォームヘルパーに
remote true
を設定する。 2.『〇〇.js.erb』ファイルを作成する。 - jsファイルに更新処理を記述する。
1. フォームヘルパーにremote true
を設定する。
今回の例ではlink_to
メソッドを例に解説します。
<%= link_to bookmarks_path(board_id: board.id), id: "js-bookmark-button-for-board-#{board.id}", class:"float-right", method: :post, remote: true do %> <%= icon 'far', 'star' %> <% end %>
このようにフォームヘルパーにオプションとしてremote: true
をつけてあげることで、フォームを送信する際にHTML形式ではなく、JS形式で送信できるようになります。
2.『〇〇.js.erb』ファイルを作成する。
『〇〇.js.erb』ファイルでは以下の3つのことが可能になります。
1. ファイルに記述したjavascriptのコードを実行できる。
2. ERBタグを使用することができる。
3. インスタンス変数を使用することだできる。
これらの特徴により、画面遷移なしで一部の読み込みだけするような処理を簡単に記述することができます。
3. jsファイルに更新処理を記述する。
$("#js-bookmark-button-for-board-<%= @board.id %>").replaceWith("<%= j(render('boards/bookmark', board: @board)) %>");
上記のようなコードを書くだけで、画面遷移なしでブックマークの解除が実装できます。
Ajax実装のまとめ
- フォームヘルパーに、
remote: true
を記述するとJS形式に変換してくれる。 - コントローラーの処理が終わると、『〇〇.js.erb』ファイルがレンダリングされる。
- 『〇〇.js.erb』では、javascriptやERBなどが使え、ページの一部だけを更新する処理を記述することができる。
参考にしたサイト
has_many throuth
ユーザーのお気に入りした掲示板を取得したいとき、中間テーブルは外部キーしか保存されていないので、
map
メソッドを使い、変換する作業が必要です。
mapメソッドとは
mapメソッドは、配列の全要素にブロック中の処理で変換を行なった、 新しい配列を作る。
配列.map do |変数| 変換処理 end
mapメソッドを使うとあまり直接的ではないのと、このコードをビューに落とし込むのも大変になります
そこで、has many through
を使えば、ユーザーのお気に入りした掲示板を直接アソシエーションで取得することができます。
has_many :bookmarks_boards, through: :bookmarks, source: :board
bookmarksメソッド(through:で定義)を実行し、それで得られたBookmarksのインスタンスデータのひとつひとつの要素に対してboardメソッド(source:で定義)を実行する。
バリデーション uniqueness
ブックマーク機能を作る際、unique制約が必要だったので理解を深めるためにまとめます!
なぜunique制約が必要なのか
ブックマーク機能は、どのユーザーが、どの掲示板をブックマークしたという関係性を保存することです。
解除する際は保存したデータを消す必要があり、データが重複していると消したはずなのに同じデータが残ってしまうことがあり、一意性が失われてしまいます。
uniquenessとは
このヘルパーは、オブジェクトが保存される直前に、属性の値が一意(unique)であり重複していないことを検証する。
class Bookmark < ApplicationRecord validates :user_id, uniqueness: true end
上記のコードだと、1ユーザーに対して1つしかブックマークできないようになっています。このままでは作りたい要件とは異なってしまいます。
そこでscope
を使用します。
scope
とはuniqueness
に用意されているオプションで、範囲を指定して、一意かどうかをチェックしてくれます。
class Bookmark < ApplicationRecord validates :user_id, uniqueness: { scope: :board_id } end
上記のコードは、1ユーザーが1掲示板に1ブックマークという範囲を指定しています。
:scopeを用いる一意性バリデーションの違反を防止する目的でデータベース側に制約を作成したい場合は、データベース側で両方のカラムにuniqueインデックスを作成しなければなりません。
class CreateBookmarks < ActiveRecord::Migration[5.2] def change create_table :bookmarks do |t| t.references :user, foreign_key: true t.references :board, foreign_key: true t.timestamps end add_index :bookmarks, [:user_id, :board_id], unique: true end end