はじめに
ActiveAdminの使用を続ける中でカスタマイズ時に頻繁に使用する記法が見えてきたので、それらを詰め込んだコードサンプルを掲載します。
コードサンプルの特徴
- ActiveAdminで
app/admin/...
に配置する1ファイルとして作成した - 私がよく書く記法を詰め込み、TIPSとしても読めそうな内容にした
- テンプレートファイルにも流用できる構成を目指した
- なるべく公式ドキュメントに沿ったが、一部に公式外の記法も混ぜた
想定する読者
- ActiveAdminを使ったことがある人
- インストール方法については記載しません
- ActiveAdminの便利な記法やカスタマイズ方法を探している人
バージョン情報
記事公開時点の最新バージョンを使用します。
- Ruby:
3.3.1
- Ruby on Rails:
7.1.3
- ActiveAdmin:
3.2.0
サンプルのテーブル構成
今回は図の中央にある company_users
の管理画面を作成します。
has_many / belongs_toの両方を持っている点が特徴です。
class CompanyUser
belongs_to :company
has_many :roles
# 自動で提案されるRansack用のメソッドは省略
end
サンプルファイル
# app/admin/company_users.rb
ActiveAdmin.register CompanyUser do
# 1. ヘッダーメニューの並び順
menu priority: 21
# 2. 使用可能なアクション一覧
actions :all, except: [:destroy]
# 3. 編集可能なカラム名
permit_params :company_id, :name, :active
controller do
# 4. DBからレコードを取得する際の処理を追加
def scoped_collection
super.includes(:company)
end
end
# 5. フィルターをRansackの記法でカスタム
filter :name
filter :active
filter :updated_at
filter :company_id, as: :select, collection: -> { Company.all }
filter :company_name_cont, label: '会社名 (部分一致)'
# 6-1. 一覧表示の内容をカスタム
index do
selectable_column
column :id
column :company_id do |record|
a href: admin_company_path(record.company) do
"#{record.company.name}(#{record.company.code})"
end
end
column :name
column :active
actions
end
# 6-2. 詳細表示の内容をカスタム
show do
attributes_table do
row :id
row :company_id do |record|
a href: admin_company_path(record.company) do
"#{record.company.name}(#{record.company.code})"
end
end
row :name
row :active
row :created_at
row :updated_at
end
end
# 6-3. 編集フォームの内容をカスタム
form do |f|
semantic_errors(*object.errors.attribute_names)
inputs do
input :company_id, as: :select, collection: Company.all, include_blank: '未選択'
input :name
input :active
end
actions
end
end
動作確認のために作成したリポジトリ
解説
1. ヘッダーメニューの並び順
menu priority: 21
menu
を省略した場合の並び順は辞書順(アルファベット順)です。メニューには求めている並び順があると思いますので必ず設定した方がいいと思います。
「CompaniesとCompanyUsersの間にメニューを追加したい」というケースは結構ありますので、連番(1→2→3)にせず、飛び番(11→21→31)のように設定しておいて間に挿入できるようにしておくのが好みです。
2. 使用可能なアクション一覧
actions :all, except: [:update, :destroy]
公式ドキュメント Disabling Actions on a Resource
使用可能なアクションを変更する方法は「許可したいアクションを列挙(=ホワイトリスト方式)」と「許可したくないアクションを列挙(=ブラックリスト方式)」の2種類が用意されています。
公式ドキュメントには後者(ブラックリスト)しか見つけられませんでしたが、前者(ホワイトリスト)も動作しました。
# 設定可能な値
[:index, :new, :create, :edit, :update, :destroy]
# 閲覧のみ可能にする (ホワイトリスト方式の例)
actions :index, :show
# 削除を不可にする (ブラックリスト方式の例)
actions :all, except: [:destroy]
無効にしたアクションはリンクも自動で消えてくれるので結構便利です。
3. 編集可能なカラム名
permit_params :company_id, :name, :active
公式ドキュメント Setting up Strong Parameters
StrongParameter (変更してよいパラメータを制御するためのRailsが提供している機能) の設定です。これを設定しないとレコードを作成・更新することができなくなるので必ず設定します。
4. DBからレコードを取得する際の処理を追加
controller do
def scoped_collection
super.includes(:organization)
end
end
公式ドキュメント Customizing resource retrieval
N+1問題の対策やwhere句の追加で活躍する書き方です。
以下にさらに複雑なサンプルを記載します。
controller do
def scoped_collection
records = super
# active=trueのレコードだけを取得する。
records = records.where(active: true)
# N+1問題も対策する。
records = records.includes(:company)
# show画面の場合にのみrolesもN+1問題を対策する。
if action_name == 'show'
records = records.includes(:roles)
end
# 戻り値を設定する (忘れがちなので注意)
records
end
end
5. フィルターをRansackの記法でカスタム
filter :name
filter :active
filter :updated_at
filter :company_id, as: :select, collection: -> { Company.all }
filter :company_name_cont, label: '会社名 (部分一致)'
一覧画面の右側のフィルターをカスタマイズするための記法です。
基本的にはfilter :カラム名
のように書くことが多いですが、セレクトやラジオを使ったり、関連レコードを条件に指定したりする方法も用意されています。
セレクトやラジオを使用する場合は collection -> { Company.all }
のようにモデル一覧を渡すか、collection: -> { Company.all.map { |company| [company.name, company.id] } }
のように[表示名, id]
の配列を設定してください。
関連レコードを条件に指定する場合はRansackのドキュメントを読むと理解が深まると思います。
6-1. 一覧表示の内容をカスタム
index do
selectable_column
column :id
column :company_id do |record|
a href: admin_company_path(record.company) do
"#{record.company.name}(#{record.company.code})"
end
end
column :name
column :active
actions
end
公式ドキュメント Customizing the Index Page
index do~end
を省略した場合、一覧画面には全カラムが表示されます。
表示内容をカスタマイズしたり、表示するカラムを絞ったりする場合にこれを記述します。
便利と思っている記法はcompany_id
のところです。今回のサンプルでは会社名+会社コードを表示し、さらにリンクにしてみました。
6-2. 詳細表示の内容をカスタム
show do
attributes_table do
row :id
row :company_id do |record|
a href: admin_company_path(record.company) do
"#{record.company.name}(#{record.company.code})"
end
end
row :name
row :active
row :created_at
row :updated_at
end
end
公式ドキュメント Customize the Show Page
やっていることは一覧画面と同じです。
サンプルには載せませんでしたが、panelやsidebarを活用することでたくさんの情報を体系的に表示することも可能です。
6-3. 編集フォームの内容をカスタム
form do |f|
semantic_errors(*object.errors.attribute_names)
inputs do
input :company_id, as: :select, collection: Company.all, include_blank: '未選択'
input :name
input :active
end
actions
end
semantic_errors以外はこれまでに紹介した機能と概ね同じ記入です。
company_idのcollectionは5. フィルターをRansackの記法でカスタムを参照してください。
semantic_errors(*object.errors.attribute_names)
はバリデーションエラーが画面に表示されないケースの対策です。公式ドキュメントに書かれていませんが、便利なのでこれを毎回書くようにしています。
「保存ボタンを押したのに画面が動かない (=保存に失敗したのにエラーメッセージが表示されないため、画面が動かないように見える)」が発生することがありました。関連レコードやフォームから変更できないカラムでバリデーションエラーが発生した場合にこれが発生します。個別に対応するのが正しいとは思いますが、とりあえずこの記法で全エラーを表示させておけば問題ないケースが多いため、いつもこれを記述するようにしています。
ちなみにsemantic_errors
のデフォルトの動作は:baseのみのバリデーションエラーを表示することです。
Qiita参考記事
GitHub Issue
おわりに
Ruby on Railsで「最速でデータを閲覧・変更できる管理画面が欲しい」と思った際、最初に思い浮かぶ候補はActiveAdminだと思います。
ActiveAdminに対しては「カスタマイズ性が悪い」とネガティブな意見を聞くこともあります。そんな時に対応策としてこの記事が活用できたらと考えて作成してみました。
実装速度に魅了されている私はメリット・デメリットを捉えながら今後もActiveAdminの使用を続けていくことになりそうです。
v4に向けた動き(v4.0.0.beta1のリリースノート)もあるようですし、今後の動向も楽しみですね。