accepts_nested_attributes_for with belongs_to polymorphic

后端 未结 4 663
渐次进展
渐次进展 2020-11-27 10:37

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

class Contact 

        
相关标签:
4条回答
  • 2020-11-27 11:03

    Just figured out that rails does not supports this kind of 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 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.

    0 讨论(0)
  • 2020-11-27 11:09

    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?"

    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:

    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:

    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
    
    0 讨论(0)
  • 2020-11-27 11:17

    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
    
    0 讨论(0)
  • 2020-11-27 11:18

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

    STEP 1. To begin, here is the full 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, that's nothing really new. However you might wonder, where does polymorph_type come from and how is its value set? It's part of the underlying database record since polymorphic associations add <association_name>_id and <association_name>_type columns to the table. As it stands, when build_associable executes, the _type's value is nil.

    STEP 2. Pass in and Accept the Child Type

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

    # 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?)

    0 讨论(0)
提交回复
热议问题