accepts_nested_attributes_for для ссылки на существующую запись, а не для создания новой

У меня есть следующие модели

class Order < AR::Base
  has_many :products

  accepts_nested_attributes_for :products
end

class Product < AR::Base
  belongs_to :order
  has_and_belongs_to_many :stores

  accepts_nested_attributes_for :stores
end

class Store < AR::Base
  has_and_belongs_to_many :products
end

Теперь у меня есть вид заказа, где я хочу обновить магазины для продукта. Дело в том, что я хочу подключать продукты только к существующим магазинам в моей базе данных, а не создавать новые.

Моя форма в представлении заказа выглядит так (используя Formtastic):

= semantic_form_for @order do |f|
  = f.inputs :for => :live_products do |live_products_form|
    = live_products_form.inputs :for => :stores do |stores_form|
      = stores_form.input :name, :as => :select, :collection => Store.all.map(&:name)

Хотя он вложенный, он отлично работает. Проблема в том, что когда я выбираю магазин и пытаюсь обновить заказ (а вместе с ним и продукты и магазины), Rails пытается создать новый магазин с таким именем. Я хочу, чтобы он просто использовал существующий магазин и подключил к нему продукт.

Любая помощь приветствуется!

РЕДАКТИРОВАТЬ 1:

В конце концов я решил эту проблему очень грубым способом:

# ProductsController

def update
  [...]

  # Filter out stores
  stores_attributes = params[:product].delete(:stores_attributes)

  @product.attributes = params[:product]

  if stores_attributes.present?
    # Set stores
    @product.stores = stores_attributes.map do |store_attributes|
      # This will raise RecordNotFound exception if a store with that name doesn't exist
      Store.find_by_name!(store_attributes[:name])
    end
  end

  @order.save

  [...]
end

РЕДАКТИРОВАТЬ 2:

Решение Пабло намного элегантнее, и его следует предпочесть моему.


person Manuel Meurer    schedule 12.11.2010    source источник
comment
После просмотра документов для a_n_a_f (api.rubyonrails.org/classes/ActiveRecord/NestedAttributes /) Сначала я обрадовался, увидев параметр update_only, но быстро понял, что нет способа сделать то, что я хочу (update_only обновляет существующие объекты перед созданием новых).   -  person Manuel Meurer    schedule 23.11.2010


Ответы (2)


Попробуйте реализовать :reject_if, который проверяет, существует ли уже Store, а затем использует его:

class Product < AR::Base
  belongs_to :order
  has_and_belongs_to_many :stores

  accepts_nested_attributes_for :stores, :reject_if => :check_store

  protected

    def check_store(store_attr)
      if _store = Store.find(store_attr['id'])
        self.store = _store
        return true
      end
      return false
    end
end

У меня этот код отлично работает в текущем проекте.

Пожалуйста, дайте мне знать, если вы нашли лучшее решение.

person Pablo    schedule 03.05.2011
comment
Очень умный! Я решил проблему гораздо менее элегантным способом (я отредактирую свой вопрос, чтобы показать это), но ваше решение должно работать лучше. - person Manuel Meurer; 06.05.2011
comment
Это не имеет смысла для меня. self в check_store является продуктом... и продукт не имеет отношения store (он HABTM :хранит). Так что же на самом деле делает этот код? Кроме того, он не обновляет найденный магазин. - person davemyron; 16.06.2011
comment
Я настроил его для собственного использования, найдя существующую связанную запись и обновив ее атрибуты в reject_if. Выглядит халтурно, конечно, но это сработало. - person davemyron; 16.06.2011
comment
Кто-нибудь находит решение с тех пор? - person gaetanm; 21.02.2016

У меня была та же проблема, и я решил ее, добавив :id в список вложенных параметров.

def family_params
  params.require(:family).permit(:user_id, :address, people_attributes: [:id, :relation, :first_name, :last_name)
end
person timeon    schedule 14.03.2016
comment
Я написал свой вопрос задолго до выхода Strong Parameters. - person Manuel Meurer; 14.03.2016