accepts_nested_attributes_for с принадлежащим_ полиморфным - PullRequest
51 голосов
/ 19 октября 2010

Я хотел бы установить полиморфное отношение с accepts_nested_attributes_for.Вот код:

class Contact <ActiveRecord::Base
  has_many :jobs, :as=>:client
end

class Job <ActiveRecord::Base
  belongs_to :client, :polymorphic=>:true
  accepts_nested_attributes_for :client
end

Когда я пытаюсь получить доступ Job.create(..., :client_attributes=>{...} дает мне NameError: uninitialized constant Job::Client

Ответы [ 4 ]

59 голосов
/ 09 августа 2011

У меня также была проблема с «ArgumentError: Невозможно построить ассоциацию имя_модели. Вы пытаетесь создать полиморфную ассоциативную связь один к одному?»

И я нашел лучшее решение для этого видапроблемы.Вы можете использовать нативный метод.Давайте посмотрим на реализацию nested_attributes внутри Rails3:

elsif !reject_new_record?(association_name, attributes)
  method = "build_#{association_name}"
  if respond_to?(method)
    send(method, attributes.except(*UNASSIGNABLE_KEYS))
  else
    raise ArgumentError, "Cannot build association #{association_name}. Are you trying to build a polymorphic one-to-one association?"
  end
end

Так что же на самом деле нужно делать здесь?Это просто для создания сборки _ # {association_name} внутри нашей модели.Я сделал полностью рабочий пример внизу:

class Job <ActiveRecord::Base
  CLIENT_TYPES = %w(Contact)

  attr_accessible :client_type, :client_attributes

  belongs_to :client, :polymorphic => :true

  accepts_nested_attributes_for :client

  protected

  def build_client(params, assignment_options)
    raise "Unknown client_type: #{client_type}" unless CLIENT_TYPES.include?(client_type)
    self.client = client_type.constantize.new(params)
  end
end
16 голосов
/ 02 октября 2015

I Наконец-то получил это для работы с Rails 4.x.Это основано на ответе Дмитрия / ScotterC, поэтому +1 к ним.

ШАГ 1. Для начала, вот полная модель с полиморфной ассоциацией:

# app/models/polymorph.rb
class Polymorph < ActiveRecord::Base
  belongs_to :associable, polymorphic: true

  accepts_nested_attributes_for :associable

  def build_associable(params)
    self.associable = associable_type.constantize.new(params)
  end
end

# For the sake of example:
# app/models/chicken.rb
class Chicken < ActiveRecord::Base
  has_many: :polymorphs, as: :associable
end

Да, в этом нет ничего нового.Однако вы можете задаться вопросом, откуда берется polymorph_type и как устанавливается его значение?Это часть базовой записи базы данных, так как полиморфные ассоциации добавляют столбцы <association_name>_id и <association_name>_type в таблицу.В действии, когда build_associable выполняется, значение _type равно nil.

ШАГ 2. Передайте и примите дочерний тип

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

# app/views/polymorph/_form.html.erb
<%= form_for(@polymorph) do |form| %>
  # Pass in the child_type - This one has been turned into a chicken!
  <%= form.hidden_field(:polymorph_type, value: 'Chicken' %>
  ...
  # Form values for Chicken
  <%= form.fields_for(:chicken) do |chicken_form| %>
    <%= chicken_form.text_field(:hunger_level) %>
    <%= chicken_form.text_field(:poop_level) %>
    ...etc...
  <% end %>
<% end %>

# app/controllers/polymorph_controllers.erb
...
private
  def polymorph_params
    params.require(:polymorph).permit(:id, :polymorph_id, :polymorph_type)
  end

Конечно, ваши представления должны будут обрабатыватьразличные типы моделей, которые «ассоциируются», но это демонстрирует один.

Надеюсь, это поможет кому-то там.(В любом случае, зачем вам полиморфные цыплята?)

8 голосов
/ 04 августа 2011

Приведенный выше ответ великолепен, но не работает с указанной настройкой. Это вдохновило меня, и я смог создать рабочее решение:

работает для создания и обновления

class Job <ActiveRecord::Base
  belongs_to :client, :polymorphic=>:true
  attr_accessible :client_attributes
  accepts_nested_attributes_for :client

  def attributes=(attributes = {})
    self.client_type = attributes[:client_type]
    super
  end

  def client_attributes=(attributes)
    some_client = self.client_type.constantize.find_or_initilize_by_id(self.client_id)
    some_client.attributes = attributes
    self.client = some_client
  end
end
5 голосов
/ 20 октября 2010

Только что выяснил, что rails не поддерживает такого рода поведение, поэтому я нашел следующий обходной путь:

class Job <ActiveRecord::Base
  belongs_to :client, :polymorphic=>:true, :autosave=>true
  accepts_nested_attributes_for :client

  def attributes=(attributes = {})
    self.client_type = attributes[:client_type]
    super
  end

  def client_attributes=(attributes)
    self.client = type.constantize.find_or_initialize_by_id(attributes.delete(:client_id)) if client_type.valid?
  end
end

Это дает мне возможность настроить мою форму так:

Не точное решение, но идея важна.

...