dom*_*esz 54 ruby ruby-on-rails 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
Run Code Online (Sandbox Code Playgroud)
当我试图访问Job.create(..., :client_attributes=>{...}给我NameError: uninitialized constant Job::Client
Dmi*_*kin 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
Run Code Online (Sandbox Code Playgroud)
那么实际上我们需要做什么呢?只是在我们的模型中创建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
Run Code Online (Sandbox Code Playgroud)
rod*_*amn 19
我终于让它与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
Run Code Online (Sandbox Code Playgroud)
是的,这不是什么新鲜事.但是你可能想知道,它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
Run Code Online (Sandbox Code Playgroud)
当然,您的视图需要处理"可关联"的不同类型的模型,但这证明了一个.
希望这有助于那里的人.(为什么你还需要多态鸡?)
上面的答案很棒但不适用于所示的设置.它启发了我,我能够创建一个有效的解决方案:
适用于创建和更新
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
Run Code Online (Sandbox Code Playgroud)
刚刚发现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
Run Code Online (Sandbox Code Playgroud)
这让我可以像这样设置我的表单:
<%= f.select :client_type %>
<%= f.fields_for :client do |client|%>
<%= client.text_field :name %>
<% end %>
Run Code Online (Sandbox Code Playgroud)
不是确切的解决方案,但这个想法很重要.