accepted_toted_attributes_for with belongs_to polymorphic

时间:2010-10-19 13:48:58

标签: ruby-on-rails ruby polymorphic-associations nested-attributes

我想与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 个答案:

答案 0 :(得分:59)

我也遇到了“ArgumentError:无法建立关联model_name。”您是否尝试构建多态一对一关联?

我为这类问题找到了更好的解决方案。您可以使用本机方法。让我们看看Rails3中的nested_attributes实现:

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

所以实际上我们需要做什么呢?只是在我们的模型中创建build _#{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

答案 1 :(得分:15)

终于让它与Rails 4.x一起使用。这是基于Dmitry / 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

当然,您的观点需要处理可以“关联”的不同类型的模型,但这证明了一个。

希望这有助于那里的人。 (为什么你还需要多态鸡?)

答案 2 :(得分:8)

上面的答案很棒,但不适用于所示的设置。它启发了我,我能够创建一个有效的解决方案:

适用于创建和更新

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

答案 3 :(得分:5)

刚才发现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

这让我可以像这样设置我的表单:

<%= f.select :client_type %>
<%= f.fields_for :client do |client|%>
  <%= client.text_field :name %>
<% end %>

不是确切的解决方案,但这个想法很重要。