问题
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
- Make a form from one end (humans)
- List out all the orcs in the form
- Select multiple orcs from that list
- Save as many records into the interactions table with
human_id
andorc_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
- creating multiple records reusing the single hidden input field.
- 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