Accepts_nested_attributes_for with belongs_to polymorphic

I would like to establish a polymorphic relation with accepts_nested_attributes_for . Here is the code:

 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=>{...} , they give me NameError: uninitialized constant Job::Client

+47
ruby ruby-on-rails nested-attributes polymorphic-associations
Oct 19 2018-10-10
source share
4 answers

I just realized that rails do not support this behavior, so I came up with the following workaround:

 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 the opportunity to customize my form as follows:

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

Not an exact solution, but an idea is important.

+5
Oct 20 2018-10-10T00:
source share

I also had a problem with "ArgumentError: Unable to create association model name. Are you trying to build a one-to-one polymorphic association?"

And I found the best solution for this kind of problem. You can use your own method. Let's look at the implementation of nested_attributes, inside 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 

So what do we need to do here? It is easy to create build _ # {association_name} inside our model. I made a full working example below:

 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 
+55
Aug 08 2018-11-21T00:
source share

I finally got this to work with Rails 4.x. This is based on Dmitry / Scott's answer, therefore +1 to them.

STEP 1. To get started, here is a complete model with polymorphic association:

 # 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, this is nothing new. However, you may wonder where polymorph_type comes from and how is its value set? This is part of the database entry because polymorphic associations add the columns <association_name>_id and <association_name>_type . However, when build_associable is executed, the value of _type is nil .

STEP 2. Skip and accept the type of child

Submit the form in the child_type form along with typical form data, and your controller should enable it when checking strong parameters.

 # 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 views will have to handle the various types of models that are β€œrelated”, but this demonstrates one.

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

+10
Oct 02 '15 at 20:29
source share

The above answer is great, but does not work with the setting shown. This inspired me, and I was able to create a working solution:

works to create and update

 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 
+8
Aug 04 2018-11-11T00:
source share



All Articles