代码之家  ›  专栏  ›  技术社区  ›  dombesz

接受具有属于多态性的嵌套属性

  •  51
  • dombesz  · 技术社区  · 15 年前

    我想建立一个多态关系 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 回复  |  直到 15 年前
        1
  •  59
  •   ScotterC    13 年前

    我还遇到了“ArgumentError:Cannot build association model_name”的问题。你想建立一个多态的一对一关联吗?”

    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
    

    那么实际上我们需要在这里做什么呢?只是为了在我们的模型中创建构建关联名。我在下面做了一个很好的例子:

    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
    
        2
  •  19
  •   rodamn    10 年前

    最后 让它与Rails 4.x一起工作。这是基于Dmitry/ScotterC的答案,所以对他们来说是+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 .

    第二步。传入并接受子类型

    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
    

    希望这能帮助其他人。(你为什么需要多态鸡?)

        3
  •  8
  •   MarkP    14 年前

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

    用于创建和更新

    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
    
        4
  •  5
  •   dombesz    11 年前

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

    推荐文章