A note of a person who is learning programming, SakaTaQ

ロック好きのプログラミング学習

画像複数投稿機能で使ったメソッドなどについてのまとめ

今回TECH::CAMP最終課題で使った「一つの物品に対して複数の画像を添付して出品する」際に利用することになったメソッドについてざっくりとまとめておきます。
今よりも理解が深まる時が来た時に更新予定。

accepts_nested_attributes_for

Active::Recordから継承しているメソッド。モデルの親子関係(ネストされている)を作る。
これにより1つのformで複数のテーブルのレコードを作成することができる。
モデルクラスに記述。 (ここではItemが親モデルで、Imageが子モデル)

class Item < ApplicationRecord
  has_many :images
  accepts_nested_attributes_for :images
end
class Image < ApplicationRecord
  belongs_to :item
end

最初、自分はこの記述があればhas_manyのようなassociationの記述はいらないんじゃないかと思ってましたが、当たり前ですが至る所でしっかりエラーが出ました💦
親子関係であるかと多対多などのリレーションは別。
その記述がなければメソッドのように item.images のような形で取り出すこともできない。


fields_for

associationで紐づいているモデルを1つのformで登録、更新するために使用するメソッド。
今回のバージョンで使用したform_withなどでは、ブロックで囲った中の変数(フォームビルダーオブジェクトというらしい)を生成する。通常、これに対してヘルパーメソッドであるtext_fieldやnumber_fieldを使用するように書いている。
fields_forもそれと同様の書き方でオブジェクトを生成できる。 この時、同じフォーム内で関連づけられた別のモデルオブジェクトを編集できる、とのこと。 言葉だけだとよくわからんのだけど、form_withを少し書いている人なら実際の記述を見ると分かりやすいのかもしれない。

= form_with scope: :item, model: @item, local: true, multipart: true do |f|
  = render 'layouts/error_messages', model: f.object
  = f.fields_for :item_images do |image|
    = image.label :name do
      = image.file_field :name
  = f.text_field :name, maxlength: 40,  placeholder: "商品名(40文字まで)"
  = f.text_area :description, maxlength: 1000, placeholder: "商品の詳細を入力してください(1000文字以内)"
  = f.submit "出品する"

今回のアプリケーションではerb形式ではなくhamlを使用したためそのまま書いた。
正直色々見直す部分があったのかもしれない。(scopeとか本当はいらなかったような気が...)
生成されたビルダーオブジェクトを通じてメソッドを適用しているからなのか分からないが、idやnameに関しては特に指定してがなければ勝手にform側で生成してくれる。
モデルをネストさせる時に使うので、accepts_nested_attributes_forを使用する時はセットで使うようなイメージ。


validates_associated

1つのフォームで複数のモデルを投稿した時に、関連モデルの変更にバリデーションをかける際に必要なヘルパー...
なんですが、子であるImageモデル側にvalidates :name, presence: trueあればcreateの時は問題ないみたい。ただ、updateの際はこれがないと同モデル内で変更がない場合、バリデーションが働かない。

class Item < ApplicationRecord
  has_many :images
  accepts_nested_attributes_for :images

  validates_associated :images
  validates :images
end

この記述をした当初はnew, createアクションだったため記述理由は分からなかったが、実装途中で更新や編集の際に必要だということが分かった。
また、これまでの認識ではvalidates続くバリデーション対象はカラム名と思っていたのだが、今回のことで属性名(attributes)なのではないかという認識に至った。


 

今回はここまで。
次回はこれらを使用する際に必要だったオプションや他に知ったことについて書いていこうと思います。

したらな!✋