Administrateで配列型のフィールドを扱う
Administrateは何度か紹介していますが、管理画面を作るのに便利なgemです。
以前の記事:gem administrateを使ってみた
今回は、そのAdministrateでPostgrSQLの配列型を扱うためのCustom Field TypeであるArrayTypeFieldを作ったので、それを紹介します。
Custom Field Typeとは?
Administrateでは、DBのカラムに合うように、デフォルトで様々なフィールド用の型が定義されています。 それをを使えば簡単に入力フォームが作れるのですが、複雑な条件になるようなカラムだと用途に合いません。
しかし、そういう場合はユーザーが自分用にフィールド用の型を定義することができます。それが、Custom Field Typeです。
今回定義したCustom Field Typeは、PostgreSQLの配列型に対応するためのCustom Field Typeです。
ArrayTypeFieldを定義する
では、ArrayTypeFieldを定義していきます。
自動生成
Administrateには、Custom Field Typeの雛形を作るためのジェネレータが定義されていますので、それを使います。
rails g administrate:field ArrayType
すると、いくつかファイルが作られます。
- app/fields/array_type_field.rb
- app/views/fields/array_type_field/_show.html.erb
- app/views/fields/array_type_field/_index.html.erb
- app/views/fields/array_type_field/_form.html.erb
ArrayTypeFieldクラスの修正
デフォルトだと、to_s
メソッドしかないので、もろもろ定義していきました。
choices
メソッドでは、Dashboardで渡されるオプションに応じて、選択項目用の値を整形しています。
Hash
ならばそのまま返しています。
require "administrate/field/base"
class ArrayTypeField < Administrate::Field::Base
def to_s
choices = options.fetch(:choices)
if choices.is_a? Hash
hash = choices.invert
data.map { |id| hash[id] }.join(', ')
else
data.join(', ')
end
end
def choices
choices = options.fetch(:choices)
if choices.is_a? Hash
choices
else
choices.zip(choices)
end
end
end
フォームの修正
- app/views/fields/array_type_field/_form.html.erb
このファイルを修正します。配列型のフィールドなので、複数選択可能なCheckboxにしています。
敢えて、check_box_tag
メソッドを使っています。というか使わないと複数選択可能なチェックボックスが作れません。
<div class="field-unit__label">
<%= f.label field.attribute %>
</div>
<div class="field-unit__field">
<% field.choices.each.with_index(1) do |(key, value), idx| %>
<label>
<%= check_box_tag "#{f.object_name}[#{field.attribute}][]", value, field.data.include?(value), id: "#{field.attribute}_#{idx}" %>
<%= key %>
</label>
<% end %>
</div>
これで、フォームは出来上がりました。
Dashboardの修正
では、Dashboardの修正を行います。今回は、Shopモデルのダッシュボードとします。
このShopモデルにpref_idsという出店先の都道府県を保存する配列型のカラムがあるとします。
これを以下のようにwith_options
のchoiceにHashを渡します。
require "administrate/base_dashboard"
class ShopDashboard < Administrate::BaseDashboard
ATTRIBUTE_TYPES = {
# 他の項目は省略…
pref_ids: ArrayTypeField.with_options(choices: {"北海道": 1, "青森県": 2, "岩手県": 3, …})
}.freeze
end
このように設定した状態で、入力フォームを見てみましょう!
いい感じですね。
Strong Parametersの設定
そして、忘れてはならないのが、Strong Parametersの設定です。
- app/controllers/admin/shops_controller.rb
このファイルがあるとして、このファイルを編集します。
module Admin
class ShopsController < Admin::ApplicationController
def update
requested_resource.assign_attributes(resource_params)
requested_resource.pref_ids = [] if resource_params[:pref_ids].nil?
if requested_resource.save
redirect_to(
[namespace, requested_resource],
notice: translate_with_resource("update.success"),
)
else
render :edit, locals: {
page: Administrate::Page::Form.new(dashboard, requested_resource),
}
end
end
private
def permitted_attributes
dashboard.permitted_attributes << [{ pref_ids: [] }]
end
end
end
permitted_attributes
メソッドをオーバーライドしておきます。
また、update
メソッドも変更しています。これは、チェックボックスを全て外した場合に値が更新されないのを防ぐための措置を入れています。
これで、配列型を扱うことができるようになりました!
保存後の画面
フォームにて、チェックを入れて保存してみたら、以下のようにカンマ区切りで表示されるようになりました。
これは、ArrayTypeField
クラスのto_s
メソッドでこのように表示するようにしています。
def to_s
choices = options.fetch(:choices)
if choices.is_a? Hash
hash = choices.invert
data.map { |id| hash[id] }.join(', ')
else
data.join(', ')
end
end
カンマ区切りではダメとかであれば、ここを編集して見た目を調整しましょう。
まとめ
Administrateだと配列型でも怖くありません。 多少面倒くさいところはありますが、管理画面でデフォルトで扱える型に縛られてしまうほうが、 DBのパフォーマンスを発揮できなかったり、後々の実装で困ったりすると思うので、 自由度の高いライブラリを使うほうがいいかなと思います。