ruby on railsのActiveAdminをカスタマイズする備忘録
どういった記事なのか
ruby on railsにおいて簡単に管理画面を実装できるActiveAdminではあるが、案件に応じて多くのカスタマイズを必要とするが失念しがち
カスタマイズの内容を別の案件で適応時に簡単に再現できるよう、ソースのメモを備忘録として記事に残す
どんな人間が書いているか
- プログラマー歴10年ちょいの初心者
- ITに関わった当初はテストエンジニア、その後プログラマー・SEに転身
- C# -> php -> ruby とプログラム未経験から職場とともにメイン言語を渡り歩いてきた
メニュー関連の制御
メニューのラベルを指定
- メニューのラベルを変更する場合、modelのリソースに「menu :label 'hoge'」を追記する
例) Userのメニューを「フロントユーザー」と表示する
ActiveAdmin.register User do
menu :label 'フロントユーザー'
end
メニューの並び順を制御
- それぞれのメニューの並び順を指定する場合、modelのリソースに「menu :priority xx」を追記する(デフォルトは10扱い)
例) Userのメニューを1番上に表示する
ActiveAdmin.register User do
menu :priority 1
end
メニューをグループ化
- メニューをグループ化して階層表示する場合、modelのリソースに「menu :parent 'hoge'」を追記する
例) Bookメニューの下にAuthorメニューを加える
ActiveAdmin.register Author do
menu :parent 'Book'
end
メニューへ表示したくないリソース
- メニューに表示したくない場合、modelのリソースに「menu :false」を追記する
例) Userのメニューを表示しない
ActiveAdmin.register User do
menu :false
end
もっと簡単に複雑なメニュー制御を行いたい場合のgem
許可する操作を指定
- modelのリソースにおいて、操作(一覧表示、登録、詳細表示など)を制限する場合は該当のmodelのリソースに許可するactionsを追記して制御する
記述内容 | 許可対象 |
---|---|
:index | 一覧 |
:show | 詳細 |
:new | 作成画面 |
:create | 作成 |
:edit | 編集 |
:update | 更新 |
:destroy | 削除 |
例) Userに対しては一覧表示と詳細表示のみ許可する
ActiveAdmin.register User do
actions :index, :show
end
取り扱うレコードに条件を指定する
- 特定の条件にマッチしたレコードのみを管理画面で扱いたい場合、modelのリソースにcontrollerを定義のうえ、scoped_collectionのメソッドをオーバーライドすることで実現できる
例) 管理画面からの登録したUser(users.is_register_admin = 1で定義)のみを表示する
controller do
def scoped_collection
User.where(is_register_admin: 1)
end
end
一覧画面の制御
一覧に表示する項目を制御
- modelのリソースにindexを定義して一覧画面に表示したい内容を記述する
記述内容 | 表示対象 |
---|---|
selectable_column | 一括操作用のチェックボックス |
id_column | ID列 |
column :hoge | 指定column列 |
actions | 編集、削除などの操作ボタン列 |
例) Userの一覧表示の項目はIDと名前とメールアドレスのみにする
index do
id_column
column :name
column :email
actions
end
表示項目の値を置換して表示
- columnの値を置換して表示することも出来る
例) Userが管理画面からの登録か(users.is_register_adminで定義)を日本語で表示する
index do
...
column '登録元' do |u|
if u.is_register_admin == 0
'フロントから登録'
elsif c.order_status == 1
'管理画面から登録'
end
end
actions
end
CSVに出力される項目を制御
- modelのリソースにcsvを定義してCSVで出力したい内容を記述するが、ここではindexと違いid_columnやactionsは使用しない
例) UserのCSV出力項目はIDと名前とメールアドレスのみにする
csv do
column :id
column :name
column :email
end
一覧画面における検索項目を制御
- modelのリソースにfilterを記述することにより、一覧画面での検索項目を制御することができる
例) Userの検索は名前のみにする
filter :name
例) Userの検索において管理画面から登録か(users.is_register_adminで定義)のプルダウン検索を追加する
filter ...
filter :is_register_admin, as: :select, collection: [['フロントから登録', 0], ['管理画面から登録', 1]]
登録画面の制御
登録時に入力できる項目を制御
- modelのリソースにformを定義して登録・編集画面に表示したい内容を記述する
記述内容 | 表示対象 |
---|---|
f.semantic_errors | 入力エラーの表示枠 |
f.inputs | 入力フィールド |
f.input :hoge | 指定columnの入力フィールド |
f.actions | 登録、キャンセルボタン |
例) Userの登録項目は名前とメールアドレスのみにする
form do |f|
f.semantic_errors
f.inputs do
f.input :name
f.input :email
end
f.actions
end
登録項目をグループ化して表示する
- 登録項目をグループ化して表示したい場合、formのinputsに名前を付けて記述する
例) Userの登録項目を種類ごとにグループ化して表示する
form do |f|
f.semantic_errors
f.inputs '基本情報' do
f.input :name
f.input :email
end
f.inputs '趣味・嗜好' do
f.input :hobby
f.input :preference
end
f.actions
end
show do
attributes_table title: '基本情報' do
row :name
row :email
end
attributes_table title: '趣味・嗜好' do
row :hobby
row :preference
end
end
hidden項目で固定の値を引き渡す
- 管理画面からの登録時は特定のcolumnに固定の値を渡したいなどの場合は、hiddenで値を登録する方法がある
ただし、そのレコード以外は管理対象外にしなければ編集時に値が上書きされるので注意
例) Userの登録時にtypeの値を固定で引き渡す
class User < ApplicationRecord
enum type: { hogehoge: 0, fugafuga: 1 }
end
form do |f|
f.semantic_errors(*f.object.errors.keys)
f.inputs do
f.input :type, :as => :hidden, input_html: {value: 'hogehoge'}
...
end
f.actions
end
# typeがhogehogeのレコードのみを扱う
controller do
def scoped_collection
Notification.where(type: 'hogehoge')
end
end
子テーブルのレコードも同時に登録する
- 親テーブルと同じ登録画面内で子テーブルのレコードを登録する場合、親modelのリソースのform内に子modelの入力も記載して、permit_paramsへ子model用のパラメータを追記する
例) Productの子modelであるProductCategories(id, product_id, category_id)を複数件登録可能とする
class Product < ApplicationRecord
has_many :product_categories, dependent: :destroy_async
accepts_nested_attributes_for :product_categories, allow_destroy: true
validate :product_categories_check
def product_categories_check
if product_categories.size == 0
errors.add :base, 'カテゴリの設定は必須です'
end
end
end
permit_params ...
product_categories_attributes: [:id, :category_id, :_destroy]
form do |f|
f.semantic_errors(*f.object.errors.keys)
f.inputs 'カテゴリ設定' do
# allow_destroyとnew_recordをtrueにして削除と追加を許可
f.has_many :product_categories, allow_destroy: true, heading: false, new_record: true do |ec|
ec.input :category_id, as: :select, include_blank: false, collection: Category.all.order(sort: :DESC).map { |x| [x.name, x.id] }
end
end
f.inputs '商品情報' do
...
end
f.actions
end
show do |product|
attributes_table title: 'カテゴリ設定' do
table_for product.product_categories do |_c|
column :category
end
end
attributes_table title: '商品情報' do
...
end
end
詳細画面の制御
詳細に表示する項目を制御
- modelのリソースにshowを定義して詳細画面に表示したい内容を記述する
記述内容 | 表示対象 |
---|---|
attributes_table | デフォルト外観の維持 |
row :hoge | 指定column行 |
active_admin_comments | コメントフォーム |
例) Userの詳細画面の項目はIDと名前とメールアドレスと登録日時のみにして、コメントを許可する
show do
attributes_table do
row :id
row :name
row :email
row :created_at
end
active_admin_comments
end
actionの前に制御を入れる
- 登録や更新前に独自の操作を入れる場合、modelのリソースにcontrollerを定義のうえ、createやupdateのメソッドをオーバーライドすることで実現できる
例) Userの登録前にUserにメールを送信する
controller do
def create
user_params = params[:user]
UserMailer.send_user_for_admin(user_params).deliver
super
end
end
例) Userの更新時にパスワードの入力がないならパスワードを既存のままにする
controller do
def update
if params[:user][:password].blank? && params[:user][:password_confirmation].blank?
params[:user].delete("password")
params[:user].delete("password_confirmation")
end
super
end
end
独自のactionの追加
- 一覧や登録などの固有のaction以外に独自のactionを追加したい場合は、modelのリソースにmember_actionで定義する
例) Userの子modelであるUserCommentsを承認するアクション(approve_comments)をgetアクションで追加
show do |f|
attributes_table do
...
# Commentsを一覧表示して承認ボタン用のhtmlを読みこみ
panel '' do
render partial: "comment_title", locals: { f: f }
table_for user.user_comments do
column :body
column :created_at
column :is_approved
end
end
end
# 独自のアクションを追加
member_action :approve_comments, :method => :get do
UserComment.where(user_id: params[:id]).where(is_approved: 0).each do |comment|
comment.is_approved = 1
comment.save
end
user = Therapist.find(params[:id])
redirect_to url_for({:action => :show}), :notice => 'コメントを承認しました'
end
<h3>
ユーザコメント
<label>
<%
# すべてのコメントが承認済みなら承認ボタンを表示させない
is_approved = 1
cnt = 0
UserComment.where(user_id: f.id).each do |comment|
if comment.is_approved != 1
is_approved = 0
cnt += 1
end
end
if is_approved == 1
%>
ステータス:承認済
<%
elsif cnt > 0
%>
ステータス:未承認<a href="/admin/users/<%= f.id %>/approve_comments">承認する</a>
<%
end
%>
</label>
</h3>
Active Storageとの連携
一覧画面にサムネイル画像を表示
- modelにhas_one_attachedで定義した画像ファイルを一覧表示でサムネイル表示する場合、modelのリソースのindexに表示内容を追記する
例) Userの画像をサムネイル表示する
class User < ApplicationRecord
has_one_attached :image
end
index do
...
column :image do |f|
f.image.present? ? image_tag(f.image, height: '40') : ''
end
...
end
登録画面で画像の登録と削除機能を追加
- modelにhas_one_attachedで定義した画像ファイルを登録画面で登録、削除するためには、modelへ削除用のメソッドを定義したうえで、modelのリソースのformでそれを利用するよう表記する
class User < ApplicationRecord
has_one_attached :image
# 画像登録時のvalidate
validate :image_presence
def image_presence
errors.add(:image, 'にはjpegまたはpngファイルを添付してください') if image.attached? && !image.content_type.in?(%('image/jpeg image/png'))
end
# 画像削除用の記述
attr_accessor :remove_image
before_validation { image.purge if @remove_image.try!(:==, "1") }
def remove_image
@remove_image || false
end
def remove_image=(value)
attribute_will_change!('remove_image') if remove_image != value
@remove_image = value
end
end
permit_params ...
:image :remove_image
# multipart: true で画像送信を許可
form html: { multipart: true } do |f|
f.inputs do
...
f.input :image, as: :file, hint: f.object.image.present? ? image_tag(f.object.image) : nil
if f.object.image.present?
f.input :remove_image, as: :boolean, required: :false, label: '画像を削除する'
end
...
end
f.actions
end
詳細画面に画像を表示
- modelにhas_one_attachedで定義した画像ファイルを詳細画面で表示する場合、modelのリソースのshowに表示内容を追記する
例) Userの画像をサムネイル表示する
class User < ApplicationRecord
has_one_attached :image
end
show do
attributes_table do
...
row :image do |f|
f.image.present? ? image_tag(f.image) : ''
end
...
end
end
Discussion