質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.22%

accepts_nested_attributes_forを使った多対多モデルの重複保存を防ぎたい

受付中

回答 0

投稿

  • 評価
  • クリップ 0
  • VIEW 590

o.hiro

score 5

 前提・実現したいこと

Productモデル(多)、Quantityモデル(中間)、Ingredientモデル(多)があります。

Productモデルを保存する際に、中間モデルとなるQuantityモデル、多対多のもう1方の多モデルである、Ingredientモデルへの同時保存をさせたいのですが、

その際、Ingredientモデルではすでにある名前(:name)の場合は新しく登録せず、ない場合のみ登録するというようなことをしたいと考えています。

 現状

現在の状況としては、Productモデルを更新するフォームを作成し、中間モデルとなるQuantityモデル、多対多のもう1方の多モデルである、Ingredientモデルへの同時保存ができる状態です。

しかし、名前(:name)が重複しても保存できてしまう状態です。

 自分でやったこと

調べてみてfind_or_create_byをどこかで使用するとわかったので、
https://learn.co/lessons/has-many-through-forms-rails のサイトのCREATE NEW CATEGORYの項目を参考に以下の内容をProduct.rbに記述しました。
しかし、名前が重複されて保存される状態は変わらないです。

Product.rb

    def ingredient_attributes=(ingredient_attributes)
        ingredient_attributes.values.each do |ingredient_attribute|
            ingredient = Ingredient.find_or_create_by(ingredient_attribute)
            Quantity.ingredient << ingredient
        end
    end

以下、作成したコードとなります。

 該当するコード

product.rb

class Product < ApplicationRecord

    has_many :quantities
    has_many :ingredients, through: :quantities
    accepts_nested_attributes_for :quantities, reject_if: :all_blank, allow_destroy: true
    accepts_nested_attributes_for :ingredients

    def ingredient_attributes=(ingredient_attributes)
        ingredient_attributes.values.each do |ingredient_attribute|
            ingredient = Ingredient.find_or_create_by(ingredient_attribute)
            Quantity.ingredient << ingredient
        end
    end

end

quantity.rb

class Quantity < ApplicationRecord
  belongs_to :product
  belongs_to :ingredient
  accepts_nested_attributes_for :ingredient, reject_if: :all_blank

end

ingredient.rb

class Ingredient < ApplicationRecord
    has_many :quantities
    has_many :products, through: :quantities
end

products_controller.rb

    def detail
        @product = Product.find(params[:id])
        @photo = Photo.new
    end

    def update
        @product = Product.find(params[:id])
        if @product.update(product_params)
            redirect_back(fallback_location: setting_detail_product_path(@product), notice: "更新できました")
        end
    end

    private

    def product_params
        params.require(:product).permit(
            quantities_attributes:[:id, :ingredient, :ingreident_id, :product_id, :unit, :amount, :_destroy, 
                ingredient_attributes:[:id, :_destroy, :ingreident_id, :name]
            ]
            )
    end

products/detail.html.erb

<%= simple_form_for @product do |f| %>
    <%= link_to_add_association 'add ingredient', f, :quantities, 
    'data-association-insertion-node' => "#product-ingredients ol", 'data-association-insertion-method' => "append", 
    :wrap_object => Proc.new {|quantity| quantity.build_ingredient; quantity } %>
    <div id="product-ingredients">
        <ol>
        <%= f.fields_for :quantities do |quantity| %>
            <%= render 'quantity_fields', f: quantity %>
        <% end %>
        </ol>
    </div>
<% end %>

products/_quantity_fields.html.erb

<li class="control-group nested-fields">
  <div class="controls">
    <%= f.label :amount, "Amount:" %>
    <%= f.text_field :amount %>

    <%= f.fields_for :ingredient do |quantity_ingredient| %>
        <%= f.label :set_name, "name:" %>
      <%= quantity_ingredient.text_field :name %>
    <% end %>

    <%= link_to_remove_association "remove", f %>
  </div>
</li>

解決方法をご存知の方がいらっしゃればアドバイスいただけると大変嬉しいです。
よろしくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

まだ回答がついていません

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.22%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる