1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

「Rails」の「accepts_nested_attributes_for」で苦戦したこと。

1
Last updated at Posted at 2018-06-25

■環境

  • macOS 10.13
  • Rails 5.1
  • Ruby 2.4
  • mysql 5.7

■やりたかったこと

「accepts_nested_attributes_for」と、「allow_destroy: true」

で、以下のような事例を、うまいこと解決したい!

■目標

商品を製造するときに、使用している原材料を複数の種類で登録したい。

  • 部位によって、登録する原材料が異なるため、分類して登録しなければならない。
  • 商品によっては、登録する分類と登録しない分類がある。

サンプル

▼商品

  • 商品1:みたらし団子
  • 商品2:こしあん団子
  • 商品3:ごまあん団子

▼原材料

  • 材料1:白玉粉A
  • 材料2:白玉粉B
  • 材料3:砂糖
  • 材料4:あずき
  • 材料5:ごま
  • 材料6:しょうゆ
  • 材料7:しお

▼登録する分類

  • お団子の主材料(分類:10)
  • あん用の主材料(分類:20)
  • あん用の副材料(分類:30)

*** ## ■商品と原材料の組み合わせ例

「みたらし団子」のとき

  • お団子の主材料
  • 白玉粉A
  • あん用の主材料
  • しょうゆ
  • 砂糖
  • あん用の副材料
  • しお

「こしあん団子」のとき

  • お団子の主材料
  • 白玉粉B
  • あん用の主材料
  • あずき
  • 砂糖
  • あん用の副材料
  • しお

「ごまあん団子」のとき

  • お団子の主材料
  • 白玉粉B
  • あん用の主材料
  • ごま
  • あん用の副材料
  • 砂糖
  • しょうゆ

***

■用意したテーブルの構造(ver.1.0)

[ products ] 商品マスタ
  • id
  • name(商品名)
[ product_classifications ] 原材料マスタ
  • id
  • name(原材料名)
[ main_product_classification_products ] お団子主材料の中間テーブル
  • id
  • product_id(商品テーブルのid)
  • product_classification_id(原材料テーブルのid)
[ sub_product_classification_products ] あん用主材料の中間テーブル
  • id
  • product_id(商品テーブルのid)
  • product_classification_id(原材料テーブルのid)
[ other_product_classification_products ] あん用副材料の中間テーブル
  • id
  • product_id(商品テーブルのid)
  • product_classification_id(原材料テーブルのid)
※便宜上、main、sub、otherと付けています。

#### ここまでは、ほぼ、レイルに乗せた標準の作りかなー、と思いながら....

■ふと、コードを組んでいて思ったこと

商品と原材料をつなぐ分類ごとの中間テーブルを作ったところまでは、よかったのですが、、、
「登録する分類ごとに中間テーブルを用意するのは、どうなんだろう?」
「登録する分類ごとに、ループで回してnewしたり、buildするのは、ソースが煩雑になりそうだなぁ。。。」
「なんとか1個の中間テーブルに分類を設けて、綺麗にソースを書けないかなぁ?」

というのが、事の始まりでした。

まぁ、、、要は、楽できて綺麗にできたらいいな。と(笑)

■用意したテーブルの構造(ver.2.0)

[ products ] 商品マスタ
  • id
  • name(商品名)
[ product_classifications ] 原材料マスタ
  • id
  • name(原材料名)
[ product_classification_products ] 商品と原材料の中間テーブル
  • id
  • product_id(商品テーブルのid)
  • product_classification_id(原材料テーブルのid)
  • specify(分類明細)

Modelの作りの見直し

原材料マスタmodel
class ProductClassification < ActiveRecord::Base
  belongs_to :product
end
中間テーブルmodel
class ProductClassificationProduct < ActiveRecord::Base
  belongs_to :product
  belongs_to :product_classification
end
main用の中間テーブルmodelをextendしたmodelを作る
class MainStuff < ProductClassificationProduct
end
sub用の中間テーブルmodelをextendしたmodelを作る
class SubStuff < ProductClassificationProduct
end
other用の中間テーブルmodelをextendしたmodelを作る
class OtherStuff < ProductClassificationProduct
end

拡張modelを使用して、商品マスタのhas_manyとaccepts_nested_attributes_forを設定する

商品マスタmodel
class Product < ActiveRecord::Base
  has_many :main_stuffs, -> { where(specify: 10) }
  has_many :main_product_classifications, through: :main_stuffs, source: :product_classification
  accepts_nested_attributes_for :main_stuffs, allow_destroy: true

  has_many :sub_stuffs, -> { where(specify: 20) }
  has_many :sub_product_classifications, through: :sub_stuffs, source: :product_classification
  accepts_nested_attributes_for :sub_stuffs, allow_destroy: true

  has_many :other_stuffs, -> { where(specify: 30) }
  has_many :other_stuff_product_classifications, through: :other_stuffs, source: :product_classification
  accepts_nested_attributes_for :other_stuffs, allow_destroy: true
end

けつまづいたこと

「specify」をどうやって、設定するの?
「accepts_nested_attributes_for」まかせにしたから、newやbuildみたいに設定するソースコードがない!
....うーん
....うーん、うーん
....うーん、うーん、うーん
「ひょっとして...before_saveなら、accepts_nested_attributes_forでも、走るんじゃない?」
ということで、、、

##### 中間テーブルの各extendしたmodelを以下のようにしてみました。
class MainStuff < ProductClassificationProduct
  before_save :set_specify

  def set_specify
    self.specify = 10
  end
end
class SubStuff < ProductClassificationProduct
  before_save :set_specify

  def set_specify
    self.specify = 20
  end
end
class OtherStuff < ProductClassificationProduct
  before_save :set_specify

  def set_specify
    self.specify = 30
  end
end

やった! 目標達成!

「accepts_nested_attributes_for」のすごさを改めて実感しました(~_~;;

■あとがき

調べ方が悪かったと思いますが、今回のパターンのようなことを参考にできるサイトを見つけることができなかったので、自分への備忘録として書きましたが
もしかしたら、同じようなことで悩んでいる人のお役に立てるかも、という思いも強く、Qiitaに初投稿してみました。
長々と読んでいただきありがとうございますm(_ _)m
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?