Ruby-on-rails accepts_nested_attributes_for 与belongs_to 多态

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3969025/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 23:25:33  来源:igfitidea点击:

accepts_nested_attributes_for with belongs_to polymorphic

ruby-on-railsrubypolymorphic-associationsnested-attributes

提问by dombesz

I would like set up a polymorphic relation with accepts_nested_attributes_for. Here is the code:

我想与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

When I try to access Job.create(..., :client_attributes=>{...}gives me NameError: uninitialized constant Job::Client

当我尝试访问时Job.create(..., :client_attributes=>{...}给了我NameError: uninitialized constant Job::Client

采纳答案by dombesz

Just figured out that rails does not supports this kind of behavior so I came up with the following workaround:

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

This gives me to set up my form like this:

这使我可以像这样设置表单:

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

Not the exact solution but the idea is important.

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

回答by Dmitry Polushkin

I've also had a problem with the "ArgumentError: Cannot build association model_name. Are you trying to build a polymorphic one-to-one association?"

我也遇到了“ArgumentError:无法构建关联模型名称。您是否正在尝试构建多态一对一关联?”的问题。

And I found a better solution for this kind of problem. You can use native method. Lets look to the nested_attributes implementation, inside Rails3:

我为这类问题找到了更好的解决方案。您可以使用本机方法。让我们看看 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

So actually what do we need to do here? Is just to create build_#{association_name} inside our model. I've did totally working example at the bottom:

那么实际上我们需要在这里做什么?只是在我们的模型中创建 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

回答by rodamn

I finallygot this to work with Rails 4.x. This is based off of Dmitry/ScotterC's answer, so +1 to them.

终于让它与 Rails 4.x 一起工作了。这是基于 Dmitry/ScotterC 的回答,所以 +1 给他们。

STEP 1.To begin, here is the full model with polymorphic association:

STEP 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

Yes, that's nothing really new. However you might wonder, where does polymorph_typecome from and how is its value set? It's part of the underlying database record since polymorphic associations add <association_name>_idand <association_name>_typecolumns to the table. As it stands, when build_associableexecutes, the _type's value is nil.

是的,这并不是什么新鲜事。但是您可能想知道,它polymorph_type来自哪里以及它的值是如何设置的?这是底层数据库记录的一部分,因为多态关联加<association_name>_id<association_name>_type列的表格。就目前而言,当build_associable执行时,_type的值为nil

STEP 2. Pass in and Accept the Child Type

STEP 2. 传入并接受子类型

Have your form view send the child_typealong with the typical form data, and your controller must permit it in its strong parameters check.

让您的表单视图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

Of course, your view(s) will need to handle the different types of models that are 'associable', but this demonstrates one.

当然,您的视图将需要处理不同类型的“可关联”模型,但这只是演示了其中一种。

Hope this helps someone out there. (Why do you need polymorphic chickens anyway?)

希望这可以帮助那里的人。(你为什么需要多态鸡?)

回答by MarkP

The above answer is great but not working with the setup shown. It inspired me and i was able to create a working solution:

上面的答案很好,但不适用于显示的设置。它启发了我,我能够创建一个可行的解决方案:

works for creating and updating

用于创建和更新

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