Rails 4中的嵌套简单表单 - has_many通过,保存多个记录

时间:2014-08-03 05:30:58

标签: ruby-on-rails simple-form nested-forms has-many-through

我通过关系获得了标准的has_many。 人类通过连接表交互有许多兽人。互动只是一个表格和模型;没有控制器或观点。

在Rails 4中使用simpleform gem,我想从人类页面创建一个表单,以便从所有兽人的池中选择多个兽人。提交后,我希望它在交互表中创建/更新尽可能多的记录,每个记录都包含人员ID,并且选择了多个orc id。 :

AKA列表符号

  1. 从一端制作表格(人类)
  2. 列出表格中的所有兽人
  3. 从该列表中选择多个兽人
  4. 将尽可能多的记录保存到与human_idorc_id的互动表中,因为从该列表中选择了兽人。 (human_id在这些记录中将是相同的,因为它是从给定的人类表格页面开始的)
  5. 我会尽可能多地编写整个故事的代码。请随时要求澄清,并解决任何错误以实现此目的。

    表格

    humans
      integer "id"
    
    interactions
      integer "human_id"
      integer "orc_id"
    
    
      index ["human_id", "orc_id"] 
      # This is the primary key. no normal id.
      # Is it better to have a primary id for this join table, or does it not matter?
    
    orcs
      integer "id"
    

    模型

    /models/human.rb

    class Human < ActiveRecord::Base
      has_many :interaction
      has_many :orcs, through: :interactions
    
      accepts_nested_attributes_for :interactions
    end
    

    /models/interaction.rb

    # Purely a join model and table. No controller, no scaffold.
    class Interaction <ActiveRecord::Base
      belongs_to :human
      belongs_to :orc
    
      accepts_nested_attributes_for :orc 
      # Singular to match what was specified in the belongs_to relationship?
      # Do I even need this if I'm only trying to read orcs to save their id into the interactions table, and not trying to modify orcs?
    end
    

    /models/orc.rb

    class Orc< ActiveRecord::Base
      has_many :interactions
      has_many :humans, through: :interactions
    
    end
    

    控制器

    /controllers/humans_controller.rb

    class HumansController < ApplicationController
    
      before_action :set_human,         only: [:show, :edit, :update, :destroy]
      before_action :build_interaction, only: [:new, :edit]
    
      private
        def set_human
          @human = Human.find(params[:id])
        end
    
        def human_params
          params.require(:human).permit(
                                        interaction_attributes: [:human_id, 
                                                                 :orc_ids,   # Is plural here correct?
                                                                 :_destroy]
          )
        end
    
        def build_interaction
          @interaction = @human.interactions.build
                         # Is the human instance variable valid here?
                         # How many interactions are being built here?
                         # How do I ensure there are as many interaction builds as there will be selected orcs (i.e. as many interaction records to be saved or updated)?
        end
    end
    

    /controllers/orcs_controller.rb

    class OrcsController < ApplicationController
    
      before_action :set_orc,   only: [:show, :edit, :update, :destroy]
    
      private
        def set_orc
          @orc = Orc.find(params[:id])
        end
    
        def orc_params
          params.require(:orc).permit()
        end
    
    end
    

    视图

    /views/humans/_form.html.haml

    = simple_form_for(@human, html: { multipart: true }) do |f|
      = f.simple_fields_for :interactions do |i|
        = i.hidden_field :human_id, value: @human.id
        = i.association :orc, collection: Orc.all
                        ^                        
                        # Should this be :orc_id?
                        # Does this code automatically extract the orc_id when saving to the interactions table?
    

    谢谢。

    我错过了什么? 当我提交时,我确认在交互连接表中没有创建记录。

    我认为有些挑战是

    1. 重复使用单个隐藏输入字段创建多个记录。
    2. 当交互表必须是单个orc时获取一个兽人列表(因为它是在交互模型中使用belongs_to:orc定义的)
    3. 此外,我在哪里可以找到有关如何使用复数形式的模型ID的更多信息(即简单地使用orc_ids代替orc_id,以及需要具体的后果)?

1 个答案:

答案 0 :(得分:5)

事实证明,使用simple_form这实际上相当简单(谁知道?)。它处理所有中间表的魔力(与Rails的真棒一起)。您需要做的就是:

= simple_form_for(@human, html: { Pmultipart: true }), do |f|
  = f.association :orcs

我没有真正使用HAML,因此我不确定do |f|之前的逗号。以下是我在ERB HTML中试图说的内容

<%= simple_form_for(@human) do |f| %>
  <%= f.association :orcs %>
<% end %>

然后在你的控制器的param sanitizer中:

def orc_params
  params.require(:orc).permit(orc_ids: [])
end

最后在你的模特中:

class Human < ActiveRecord::Base
  ...
  accepts_nested_attributes_for :orcs
end

那就是它!它将自动创建连接对象。你不喜欢魔法吗?

这将生成由所有兽人填充的多选字段。您可以使用f.association :orcs, as: :check_boxes轻松将其更改为复选框。