Nested Simple Form in Rails4 - has many through, save multiple records

戏子无情 提交于 2019-12-21 02:53:10

问题


I've got a standard has_many through relationship. Humans have many Orcs through a join table Interactions. The interactions is just a table and model; no controller or views.

Using the simpleform gem in Rails 4, I want to make a form from the humans page, in order to select multiple orcs out of the pool of all orcs. Once submitted, I want it to create/update as many records in the interactions table, each with the human id, and as many orc ids were selected. :

AKA list notation

  1. Make a form from one end (humans)
  2. List out all the orcs in the form
  3. Select multiple orcs from that list
  4. Save as many records into the interactions table with human_id and orc_id as there were orcs chosen from that list. (The human_id will be the same in these records, since it started from a given human's form page)

I'll code out as much of the entire story as I have. Please feel free to ask for clarifications, and fix anything wrong for accomplishing this.

Tables

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

/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

/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

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

Thank you.

What am I missing? when I submit, I confirmed that there no record created in the interactions join table.

I think some of the challenges are

  1. creating multiple records reusing the single hidden input field.
  2. obtaining a list of orcs when the interactions table must be a singular orc (since it was defined with belongs_to :orc in the interactions model)

Also, where can I find out more about how using the plural form of model ids works (i.e. simply using orc_ids instead of orc_id, and what concrete consequences that entails)?


回答1:


Turns out that with simple_form this is actually rather simple (who knew?). It handles the magic of all the intermediary tables (together with Rails's awesomeness). All you need to do is this:

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

I don't really use HAML so I'm not sure about that comma before do |f|. Here is what I'm trying to say in ERB HTML

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

Then in your controller's param sanitizer:

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

And finally in your model:

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

And that's it! It will automatically create the join objects. Don't you just love magic?

This will generate a multi-select field populated by all orcs. You can easily change this to checkboxes by using f.association :orcs, as: :check_boxes.



来源:https://stackoverflow.com/questions/25101933/nested-simple-form-in-rails4-has-many-through-save-multiple-records

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!