I'm throwing in the towel after a day of getting nowhere (and the answer will likely be annoyingly simple, but that's code).
Ok, I have 2 models, User (with Devise) and Chapter.
class User < ApplicationRecord
has_many :projects
has_and_belongs_to_many :chapters
end
class Chapter < ApplicationRecord
belongs_to :country
has_and_belongs_to_many :users
accepts_nested_attributes_for :users
end
[...]
<% @chapter.users.each_with_index do |chap_user, i| %>
<div class="row">
<div class="col-md-4 col-md-offset-4 col-xs-8 col-xs-offset-2">
<small>User <%= i + 1 %></small>
</div>
</div>
<div class="row">
<div class="col-md-2 col-md-offset-4 col-xs-8 col-xs-offset-2">
<div class="field form-group">
<%= form.label 'First Name' %>
<%= form.text_field :first_name, value: chap_user.first_name,class:"form-control"%>
</div>
</div>
<div class="col-md-2 col-md-offset-0 col-xs-8 col-xs-offset-2">
<div class="field form-group">
<%= form.label 'Last Name' %>
<%= form.text_field :last_name, value: chap_user.last_name, class:"form-control"%>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4 col-md-offset-4 col-xs-8 col-xs-offset-2">
<div class="field form-group">
<%= form.label :email %>
<%= form.text_field :email, value: chap_user.email, class:"form-control"%>
</div>
</div>
</div>
<% end %>
[...]
private # Use callbacks to share common setup or constraints between actions. def set_chapter @chapter = Chapter.find(params[:id]) end
# Never trust parameters from the scary internet, only allow the white list through.
def chapter_params
params.fetch(:chapter, {})
params.require(:chapter).permit(:city, :country_id, user_attributes: [:first_name,:last_name,:email])
end
# or...
params.require(:chapter).permit(:city, :country_id, user_ids: [])
# or...
params.require(:chapter).permit(:city, :country_id, user_attributes: [:id, :first_name])
# or...
params.require(:chapter).permit(:city, :country_id, users_attributes: [:id, :first_name])
# PATCH/PUT /chapters/1
# PATCH/PUT /chapters/1.json
def update
binding.pry
respond_to do |format|
if @chapter.update(chapter_params)
format.html { redirect_to @chapter, notice: 'Chapter was successfully updated.' }
format.json { render :show, status: :ok, location: @chapter }
else
format.html { render :edit }
format.json { render json: @chapter.errors, status: :unprocessable_entity }
end
end
end
I know this one is a bit ugly but basically the params are being submitted but users[:first_name,:last_name,:email] aren't being permitted
[1] pry(#)> chapter_params Unpermitted parameters: :first_name, :last_name, :email => "London", "country_id"=>"1"} permitted: true> [2] pry(#)> params => "✓", "_method"=>"patch", "authenticity_token"=>"9IdAqcAsVG5vvakSf7jHRk+WplN/GyVZUxmAdefUbTX/uI6IajmZPr1YSol21Zhzdl7P/lrl+SVnWr5mBMiljw==", "chapter"=>"London", "country_id"=>"1", "first_name"=>"Tim2", "last_name"=>"Heard", "email"=>"jmcgregorx@spartaglobal.com"} permitted: false>, "commit"=>"Update", "controller"=>"chapters", "action"=>"update", "id"=>"1"} permitted: false>
In a nutshell, I want to connect certain users to certain chapters through a form, but I can't figure out how to get the nested params through the chapter update/create methods. My head is a red, pulpy mess. Please help.
Since the fields you are trying to modify are for a child model, you have to tell the form that because it assumes that you're editing fields on parent model by default. The way to to that is by using #fields_for (specifically the the "One-to-Many" section).
So you'll do something along these lines (explanation below code):
<% @chapter.users.each_with_index do |chap_user, i| %>
<%= form.fields_for :users, chap_user do |user_fields| %>
<div class="row">
<div class="col-md-4 col-md-offset-4 col-xs-8 col-xs-offset-2">
<small>User <%= i+ 1 %></small>
</div>
</div>
<div class="row">
<div class="col-md-2 col-md-offset-4 col-xs-8 col-xs-offset-2">
<div class="field form-group">
<%= user_fields.label 'First Name' %>
<%= user_fields.text_field :first_name, value: chap_user.first_name,class:"form-control"%>
</div>
</div>
<div class="col-md-2 col-md-offset-0 col-xs-8 col-xs-offset-2">
<div class="field form-group">
<%= user_fields.label 'Last Name' %>
<%= user_fields.text_field :last_name, value: chap_user.last_name, class:"form-control"%>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4 col-md-offset-4 col-xs-8 col-xs-offset-2">
<div class="field form-group">
<%= user_fields.label :email %>
<%= user_fields.text_field :email, value: chap_user.email, class:"form-control"%>
</div>
</div>
</div>
<% end %>
<% end %>
What changed is I wrapped all the form inputs in a block using the #fields_for
method to specify that now we're modifying child attributes. When we do so, we also specify a variable name that will be used to generate fields for the child model's attributes. I named it user_fields
and then inside that block replaced all references of form
with user_fields
.
In your ChaptersController, you want the line that accepts the params to be:
params.require(:chapter).permit(:city, :country_id, users_attributes: [:id, :first_name, :last_name, :email])
You'd want users_attributes
and not user_attribute
because the plural "users" tells rails to expect attributes for more than one child (more than one user). Then inside that array you want to tell it all the fields to expect, including id
. Even though you're not explicitly defining a field for the ID, Rails puts one to allow you to edit attributes for children that already exist (and not only be able to create new ones). Snippet from the documentation:
Note that #fields_for will automatically generate a hidden field to store the ID of the record. There are circumstances where this hidden field is not needed and you can pass include_id: false to prevent #fields_for from rendering it automatically.
If something about this doesn't work, or if you have more questions, feel free to let me know!
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.