ActiveAdmin を導入します-関連モデルの導入

今回も引き続きActiveAdminを触っていきます。
主に、Nested Resources を触ってゆきます。

それでは本編です。

目次

実行環境

  • Rails 5.2.3

Nested Resources

Nested Resourcesは、ActiveAdmin で管理されるオブジェクトに関連づいたオブジェクトをフォームで編集するために使うようです。
書籍への著者、クラスへの人といった関係のものに使います。

1 対多 - has_many

書籍への口コミを設定します。(管理者の画面で設定するものではないという突っ込みはあるでしょうが。)

モデル定義

以下、コマンドでモデルを作成。

1
2
rails generate model review score:integer comment::text book:references
rails db:migrate

review モデルは作成されたものそのままでいいです。

app/models/review.rb
1
2
3
class Review < ApplicationRecord
belongs_to :book
end

book モデルには、has_many: reviewsを追記します。

app/models/book.rb
1
2
3
4
5
6
7
class Book < ApplicationRecord
enum published:{
:already=>1,
:unreleased=>2
}
has_many: reviews
end

ActiveAdmin 用設定

book モデルには、accepts_nested_attributes_for :reviews,allow_destroy: trueを追記します。
accepts_nested_attributes_for が無いと、(undefined method `new_record?' for nil:NilClass):みたいなエラー表示が出ます。

app/models/book.rb
1
2
3
4
5
6
7
8
class Book < ApplicationRecord
enum published:{
:already=>1,
:unreleased=>2
}
has_many :reviews
accepts_nested_attributes_for :reviews,allow_destroy: true
end

app/admin/books.rb には、以下を追記します。

1
2
3
4
5
6
f.inputs do
f.has_many :reviews ,allow_destroy: true do|t|
t.input :score
t.input :comment
end
end

親のフォームが、review を複数持っていて、allow_destroy:trueを定義することで、削除用のチェックボックスが使用できます。
追記したものが以下の通りです。

app/admin/books.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ActiveAdmin.register Book do
permit_params :title, :price, :published,reviews_attributes: [:id, :score,:comment, :_destroy]

filter :title

form do |f|
f.inputs do
f.input :title
f.input :price

f.input :published, as: :select, collection:Book.publisheds_i18n.invert
end
#追加部分
f.inputs do
f.has_many :reviews ,allow_destroy: true do|t|
t.input :score
t.input :comment
end
end
#追加部分
f.actions
end
end

以上を作成して、rails sでサーバー起動して、表示を確認。
Review の項目が作成されて、「新規に Review を作成する」ボタンが作成されました。

「新規に Review を作成する」ボタンを押すと、Score と Comment が登録できます。

1 対多での関連付けられたフォームの作成ができました。


多対多 - has_and_belongs_to_many

今度は、多対多の関係にある例として、書籍に対しての著者を設定します。

モデル定義

以下、コマンドでモデルを作成。

1
2
rails generate model author name:string
rails db:migrate

以下の操作で中間テーブルを作成。

1
2
3
rails generate migration create_authors_books author:references book:references
# 作成されたマイグレーションファイルにid: falseを付与
rails db:migrate

中間テーブルの作成方法についてはRuby on Rails での多対多のアソシエーションの設定方法を参照。

review モデルには、has_and_belongs_to_many :booksを記述します。
空の名前を登録されたくないのでvalidates :name, presence: trueも追加します。

app/models/author.rb
1
2
3
4
class Author < ApplicationRecord
validates :name, presence: true
has_and_belongs_to_many :books
end

book モデルには、has_and_belongs_to_many :authorsを追記します。

app/models/book.rb
1
2
3
4
5
6
7
8
9
10
class Book < ApplicationRecord
enum published:{
:already=>1,
:unreleased=>2
}
has_many :reviews
accepts_nested_attributes_for :reviews,allow_destroy: true

has_and_belongs_to_many :authors
end

ActiveAdmin 用設定

以下を実行して、ActiveAdmin の管理対象に author を追加。

1
rails generate active_admin:resource author

book モデルには、accepts_nested_attributes_for :authors,allow_destroy: trueを追記します。

app/models/book.rb
1
2
3
4
5
6
7
8
9
10
11
class Book < ApplicationRecord
enum published:{
:already=>1,
:unreleased=>2
}
has_many :reviews
accepts_nested_attributes_for :reviews,allow_destroy: true

has_and_belongs_to_many :authors
accepts_nested_attributes_for :authors,allow_destroy: true
end

app/admin/books.rb には、以下を追記します。

1
2
3
4
5
6
7
8
9
#permit_paramsに以下を追加
authors_attributes: [:id, :name, :_destroy]

#formの中に追加
f.inputs do
f.has_many :authors ,allow_destroy: true do|a|
a.input :name
end
end

追記したものが以下の通りです。

app/admin/books.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
ActiveAdmin.register Book do
permit_params :title, :price, :published,
reviews_attributes: [:id, :score, :comment, :_destroy],
authors_attributes: [:id, :name, :_destroy]

#省略

form do |f|
f.inputs do
f.input :title
f.input :price

f.input :published, as: :select, collection:Book.publisheds_i18n.invert
end
f.inputs do
f.has_many :reviews ,allow_destroy: true do|t|
t.input :score
t.input :comment
end
end
#追加部分
f.inputs do
f.has_many :authors ,allow_destroy: true do|a|
a.input :name
end
end
#追加部分
f.actions
end
end

以上を作成して、rails sでサーバー起動して、表示を確認。
Author の項目が作成されて、Name を入力できるようになりました。

著者を自由に入力できるのは多対多のアソシエーションを生かせていない気がします。
用意した著者から選択する形を取りたかったのですが、has_and_belongs_to_many を使用した場合はできませんでした。
用意したリストから選択し、対象との関連付けを作成するので中間モデルにアクセスする必要があります。
しかし「has_and_belongs_to_many は中間モデルを持たないので、その用途では使えない」ということで納得しました。
この操作は、次の has_many through に任せることとします。


多対多 - has_many through

今度は、has_many through でアソシエーションを作って、ActiveAdmin で管理してみます。
多対多でアソシエーションするネタが枯渇してきましたが今度はお客さんを定義します。
お客さんのリストから選択して、関連付けできるようにします。

モデル定義

以下、コマンドでモデルを作成。

1
2
rails generate model customer name:string
rails db:migrate

以下の操作で中間モデルを作成。
客と書籍は購買(purchase)でつながることにします。

1
2
rails generate model purchase customer:references book:references
rails db:migrate

customer モデルには、アソシエーションのために以下を記述します。

1
2
has_many :purchases
has_many :books, through: :purchases

空の名前を登録されたくないのでvalidates :name, presence: trueも追加します。
反映した customer モデルが以下の通りです。

app/models/customer.rb
1
2
3
4
5
class Author < ApplicationRecord
validates :name, presence: true

has_many :purchases
has_many :books, through: :purchasesend

book モデルには、アソシエーションのために以下を記述します。

1
2
has_many :purchases
has_many :customers, through: :purchases

追記した book モデルが以下の通りです。

app/models/book.rb
1
2
3
4
5
6
class Book < ApplicationRecord
#省略

has_many :purchases
has_many :customers, through: :purchases
end

Activeadmin 用設定

以下を実行して、ActiveAdmin の管理対象に customer を追加。

1
rails generate active_admin:resource customer

app/admin/customers.rb で、permit_params :name, :_destroyを記述して入力パラメーターの許可を設定します。

app/admin/customers.rb
1
2
3
4
5
6
7
8
ActiveAdmin.register Customer do

#省略

permit_params :name, :_destroy

end

book モデルには、accepts_nested_attributes_for :purchases, allow_destroy: trueを追記します。

app/models/book.rb
1
2
3
4
5
6
7
class Book < ApplicationRecord
#省略

has_many :purchases
has_many :customers, through: :purchases
accepts_nested_attributes_for :purchases, allow_destroy: true
end

accepts_nested_attributes_for の対象を中間モデルの purchases にするのがポイントです。

app/admin/books.rb には、以下を追記します。

1
2
3
4
5
6
7
8
9
#permit_paramsに以下を追加
purchases_attributes: [:id, :customer_id, :_destroy]

#formの中に追加
f.inputs do
f.has_many :purchases ,allow_destroy: true do|p|
p.input :customer ,as: :select,collection: Customer.all
end
end

select の選択肢を author の name の一覧から作成します。
追記したものが以下の通りです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
ActiveAdmin.register Book do
permit_params :title, :price, :published,
reviews_attributes: [:id, :score,:comment, :_destroy],
authors_attributes: [:id, :name, :_destroy]
purchases_attributes: [:id, :customer_id, :_destroy]

#省略

form do |f|
f.inputs do
f.input :title
f.input :price
f.input :published, as: :select, collection:Book.publisheds_i18n.invert
end
f.inputs do
f.has_many :reviews ,allow_destroy: true do|t|
t.input :score
t.input :comment
end
end
f.inputs do
f.has_many :authors ,allow_destroy: true do|t|
t.input :name
end
end
f.inputs do
f.has_many :purchases ,allow_destroy: true do|p|
p.input :customer ,as: :select,collection: Customer.all
end
end
f.actions
end
end

以上を作成して、rails sでサーバー起動して、表示を確認。
Author の項目が作成されて、Name を入力できるようになりました。

選択肢になるお客さんは、Customers の項目で作成しておきます。

作成しておくと、以下のように選ぶことができます。


今回は、ActiveAdmin で関連付けされたモデルの登録を作ってみました。
今回記事を書くに当たり、has_and_belongs_to_many と has_many through の中間テーブルの取り扱いの違いに気が付けず 4 日ほど悩んでしまいました。
似て非なる(詳しい人からすると似てないといわれるかもしれませんが)ものの区別は大事だと再認識しました。

ずいぶんかかりましたが、これで次から悩むことはなさそうです。
非 ActiveAdmin での Rails の学習にも、今回の経験が役に立つ気がします。

ではでは。