- Rails 6.0.1
- Active Admin 2.4.0
を使います。
結論
Active Adminで、Active RecordのEnums (ActiveRecord::Enum
) を用いたモデルの属性のフィルタをチェックボックス形式で作りたいときは、Enumsのkey-valueマッピングのHashを渡します。
# app/models/user.rb
class User
enum status: { active: 0, suspended: 1, inactive: 2 }
end
# app/admin/user.rb
ActiveAdmin.register User do
filter :status, as: :check_boxes, collection: User.statuses
# User.statuses #=> { active: 0, suspended: 1, inactive: 2 }
end
active / suspended で検索している |
---|
![]() |
ここから下はうまくいかないケースについて説明します。
うまくいかないケース
キーのリスト (User.statuses.keys
) だけ渡すと、キーのリストだけ渡したときに生成するフォームでサーバへサブミットすると、サーバサイドでRansackによって検索するときにうまく動きません。
# app/admin/user.rb
ActiveAdmin.register User do
filter :status, as: :check_boxes, collection: User.statuses.keys
# User.statuses.keys #=> ["active", "suspended", "inactive"]
end
suspended で検索したが active なレコードだけ表示されている |
---|
![]() |
何が問題か
生成されるフォームの構造
check_boxes
filterにEnumsのキーのリストだけを渡すと、生成されるフォームのinputタグの value
がステータスの文字列そのままになります:
<div class="check_boxes input optional filter_form_field filter_check_boxes" id="q_status_input">
<fieldset class="choices">
<legend class="label">
<label>Status</label>
</legend>
<label for="q_status_preparing">
<input type="checkbox" name="q[status_in][]" id="q_status_preparing" value="preparing"> preparing
</label>
<label for="q_status_transferring">
<input type="checkbox" name="q[status_in][]" id="q_status_transferring" value="transferring">transferring
</label>
<label for="q_status_succeeded">
<input type="checkbox" name="q[status_in][]" id="q_status_succeeded" value="succeeded">succeeded
</label>
<label for="q_status_failed"><input type="checkbox" name="q[status_in][]" id="q_status_failed" value="failed"> failed
</label>
</fieldset>
</div>
inputタグの value
が status
のキーになっています。これをサブミットすると、サーバサイドでRansackがこの値を元にレコードを検索します。このとき、Enumsの属性は本来数値で検索しますが、文字列を渡すと、Enumsの最初の要素の値(いまだと0)が使われてしまいます。このときのログは次のとおりです:
Started GET "/admin/users?q%5Bstatus_in%5D%5B%5D=active&q%5Bstatus_in%5D%5B%5D=suspended&commit=Filter&order=id_desc" for ::1 at 2019-11-22 10:24:30 +0900
Processing by Admin::UsersController#index as HTML
Parameters: {"q"=>{"status_in"=>["active", "suspended"]}, "commit"=>"Filter", "order"=>"id_desc"}
Rendering /Users/kymmt90/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activeadmin-2.4.0/app/views/active_admin/resource/index.html.arb
(0.2ms) SELECT COUNT(*) FROM (SELECT 1 AS one FROM "users" WHERE "users"."status" IN (0, 0) LIMIT ? OFFSET ?) subquery_for_count [["LIMIT", 30], ["OFFSET", 0]]
CACHE (0.0ms) SELECT COUNT(*) FROM (SELECT 1 AS one FROM "users" WHERE "users"."status" IN (0, 0) LIMIT ? OFFSET ?) subquery_for_count [["LIMIT", 30], ["OFFSET", 0]]
(0.1ms) SELECT COUNT(*) FROM "users" WHERE "users"."status" IN (0, 0)
CACHE (0.0ms) SELECT COUNT(*) FROM (SELECT 1 AS one FROM "users" WHERE "users"."status" IN (0, 0) LIMIT ? OFFSET ?) subquery_for_count [["LIMIT", 30], ["OFFSET", 0]]
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."status" IN (0, 0) ORDER BY "users"."id" desc LIMIT ? OFFSET ? [["LIMIT", 30], ["OFFSET", 0]]
Rendered /Users/kymmt90/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activeadmin-2.4.0/app/views/active_admin/resource/index.html.arb (Duration: 145.3ms | Allocations: 117153)
Completed 200 OK in 147ms (Views: 145.2ms | ActiveRecord: 0.6ms | Allocations: 118179)
こうなると、どのチェックボックスでフィルタしようとしても、つねに最初の要素だけが検索されてしまいます。
正解の方法だと、次のようなフォームが生成されます。
<div class="check_boxes input optional filter_form_field filter_check_boxes" id="q_status_input">
<fieldset class="choices">
<legend class="label">
<label>Status</label>
</legend>
<label for="q_status_0">
<input type="checkbox" name="q[status_in][]" id="q_status_0" value="0"> preparing
</label>
<label for="q_status_1">
<input type="checkbox" name="q[status_in][]" id="q_status_1" value="1"> transferring
</label>
<label for="q_status_2">
<input type="checkbox" name="q[status_in][]" id="q_status_2" value="2"> succeeded
</label>
<label for="q_status_3">
<input type="checkbox" name="q[status_in][]" id="q_status_3" value="3"> failed
</label>
</fieldset>
</div>
inputタグの value
が整数値となっているので、これをサブミットすると正しく検索できます。このときのログは次のとおりです:
Started GET "/admin/users?q%5Bstatus_in%5D%5B%5D=0&q%5Bstatus_in%5D%5B%5D=1&commit=Filter&order=id_desc" for ::1 at 2019-11-22 10:22:18 +0900
Processing by Admin::UsersController#index as HTML
Parameters: {"q"=>{"status_in"=>["0", "1"]}, "commit"=>"Filter", "order"=>"id_desc"}
Rendering /Users/kymmt90/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activeadmin-2.4.0/app/views/active_admin/resource/index.html.arb
(0.2ms) SELECT COUNT(*) FROM (SELECT 1 AS one FROM "users" WHERE "users"."status" IN (0, 1) LIMIT ? OFFSET ?) subquery_for_count [["LIMIT", 30], ["OFFSET", 0]]
CACHE (0.0ms) SELECT COUNT(*) FROM (SELECT 1 AS one FROM "users" WHERE "users"."status" IN (0, 1) LIMIT ? OFFSET ?) subquery_for_count [["LIMIT", 30], ["OFFSET", 0]]
(0.1ms) SELECT COUNT(*) FROM "users" WHERE "users"."status" IN (0, 1)
CACHE (0.0ms) SELECT COUNT(*) FROM (SELECT 1 AS one FROM "users" WHERE "users"."status" IN (0, 1) LIMIT ? OFFSET ?) subquery_for_count [["LIMIT", 30], ["OFFSET", 0]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."status" IN (0, 1) ORDER BY "users"."id" desc LIMIT ? OFFSET ? [["LIMIT", 30], ["OFFSET", 0]]
Rendered /Users/kymmt90/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activeadmin-2.4.0/app/views/active_admin/resource/index.html.arb (Duration: 142.3ms | Allocations: 117716)
Completed 200 OK in 144ms (Views: 142.2ms | ActiveRecord: 0.5ms | Allocations: 118736)