Rails 4 nested attributes and has_many :through associaton in a form
When employing a has_many :through
relationship, you need to pass the nested_attributes
through the different models, like this:
Models
class Goal < ActiveRecord::Base
has_many :milestones, inverse_of: :goal, dependent: :destroy
accepts_nested_attributes_for :milestones, :allow_destroy => true
def self.build
goal = self.new
goal.milestones.build.milestone_programs.build_program
end
end
class Milestone < ActiveRecord::Base
belongs_to :goal, inverse_of: :milestones
has_many :milestone_programs
has_many :programs, through: :milestone_programs
accepts_nested_attributes_for :milestone_programs
end
class MilestoneProgram < ActiveRecord::Base
belongs_to :milestone
belongs_to :program
accepts_nested_attributes_for :program
end
class Program
has_many :milestone_programs
has_many :milestones, through: :milestone_programs
end
Controller#app/controllers/goals_controller.rb
def new
@goal = Goal.build
end
def create
@goal = Goal.new(goal_params)
@goal.save
end
private
def goal_params
params.require(:goal).permit(milestones_attributes: [milestone_programs_attributes: [program_attributes:[]]])
end
Form#app/views/goals/new.html.erb
<%= form_for @goal do |f| %>
<%= f.fields_for :milestones do |m| %>
<%= m.fields_for :milestone_programs do |mp| %>
<%= mp.fields_for :program do |p| %>
<%= p.text_field :name %>
<% end %>
<% end %>
<% end %>
<%= f.submit %>
<% end %>
I appreciate this might not be exactly what you're looking for, but tbh I didn't read all your prose. I just gathered you needed help passing nested_attributes
through a has_many :through
relationship Rails forms for has_many through association with additional attributes?
I will be solving your problem using cocoon, a gem I created to handle dynamically nested forms. I also have an example project to show examples of different types of relationships.
Yours is not literally included, but is not that hard to derive from it. In your model you should write:
class User
has_many :users_widgets
has_many :widgets, :through -> :user_widgets
accepts_nested_attributes_for :user_widgets, :reject_if => :all_blank, :allow_destroy => true
#...
end
Then you need to create a partial view which will list your linked UserWidgets
. Place this partial in a file called users/_user_widget_fields.html.haml
:.nested-fields
= f.association :widget, :collection => Widget.all, :prompt => 'Choose an existing widget'
= f.input :weight, :hint => 'The weight will determine the order of the widgets'
= link_to_remove_association "remove tag", f
In your users/edit.html.haml
you can then write:= simple_form_for @user do |f|
= f.input :name
= f.simple_fields_for :user_widgets do |user_widget|
= render 'user_widget_fields', :f => user_widget
.links
= link_to_add_association 'add widget', f, :user_widgets
Hope this helps. Rails 5 - has_many through: and nested fields_for in forms
You're making a classic newbie misstake and using fields_for
when you just want to create an association by passing an id.
<%= form_with model: @category, local: true do |f| %>
# ...
<%= f.fields_for :category_infos do |cif| %>
<%= cif.collection_select(:language_id, Language.all, :name, :id) %>
<%= cif.text_field :label %>
<% end %>
<% end %>
While you could also pass the attributes to let a users create languages at the same time its very much an anti-pattern as it adds a crazy amount of responsibilities to a single controller. It will also create an authorization problem if the user is allowed to create categories but not languages. Use ajax to send requests to a seperate languages controller instead if you need the feature.
Rails form with nested attributes in has_many through relationship
Try setting the inverse_of option on the has_many associations
has_many :admin_host_users, class_name: 'Admin::HostUser', foreign_key: 'admin_host_id', dependent: :destroy, inverse_of: 'Admin::Host'
has_many :users, through: :admin_host_users, inverse_of: 'Admin::Host'
has_many through form and adding attribute to join table
Account model:
class Account < ActiveRecord::Base
attr_accessible :name, :description, :user_accounts_attributes
has_many :user_accounts, :dependent => :destroy
has_many :users, :through => :user_accounts
has_many :user_without_accounts, :class_name => 'User', :finder_sql => Proc.new {
%Q{
SELECT *
FROM users where id NOT IN (Select user_id from user_accounts where account_id = #{id})
}
}
accepts_nested_attributes_for :user_accounts, reject_if: proc { |attributes| attributes['user_id'] == '0' }
def without_accounts
new_record? ? User.all : user_without_accounts
end
end
In form:
= simple_form_for @account do |f|
= f.input :name
= f.input :description
- @account.user_accounts.each do |user_account|
= f.simple_fields_for :user_accounts, user_account do |assignment|
= assignment.check_box :user_id
= assignment.label :user_id, user_account.user.name rescue raise user_account.inspect
= assignment.input :account_admin
%hr
- @account.without_accounts.each do |user|
= f.simple_fields_for :user_accounts, @account.user_accounts.build do |assignment|
= assignment.check_box :user_id
= assignment.label :user_id, user.name
= assignment.input :account_admin
%hr
= f.button :submit
Rails 5 - has_many through and nested attributes forms
Explanations:
Your
request_params
permitstable_locations: [:id]
, but this only permits the following format:
but yours is showing to be:Parameters: {"utf8"=>"✓", ... "request"=>{"table_locations"=>{"id"=>"1"}, "comments"=>""}, "commit"=>"Submit"}
Parameters: {"utf8"=>"✓", ... "request"=>{"table_locations"=>["1"], "comments"=>""}, "commit"=>"Submit"}
- therefore, try this:
puts request_params
inside the create method, and you'll notice that it doesn't have table_locations values (even though you thought it's there, but it's not), because it is not "properly" whitelisted in your strong paramsrequest_params
.
- therefore, try this:
To be able to associate multiple
TableLocations
objects to a newly builtRequest
object, the format should be something like below
but from your implementation, you're doing it like this (which won't work):request = Request.new(table_location_ids: [1,2,3,4,5])
request = Request.new(table_locations: [1,2,3,4,5])
# => this will raise an error:
# ActiveRecord::AssociationTypeMismatch: TableLocation expected, got Fixnum
# however yours didn't raise an error, because it was not whitelisted in the request_params in the first place
Solution:
requests_controller.rb
def request_params
params.require(:request).permit(..., table_location_ids: [])
end
_form.html.erb<% TableLocation.all.each do |t| %>
<%= check_box_tag "request[table_location_ids][]", t.id %>
<%= t.location %>
<br />
<% end %>
Recommendation:
just in case you don't know yet that you can do the following in this way, I'll be refactoring your code to look something like this:
requests_controller.rb
_form.html.erbdef create
@table_locations = TableLocation.all
end<%= form_for @request do |f| %>
<% @table_locations.each do |table_location| %>
<%= f.check_box :table_location_ids, multiple: true, table_location.id %>
<% end %>
<% end %>
accept nested attributes for has_many & has_many through in same form_for
You have many pluralization errors. Check these changes:
class User < ActiveRecord::Base
has_many :tasks
has_many :task_items, through: :tasks #not task
accepts_nested_attributes_for :tasks #not task
accepts_nested_attributes_for :task_items #not task_item
end
class Task < ActiveRecord::Base
belongs_to :user
has_many :task_items #not task_item
end
class TaskItem < ActiveRecord::Base
belongs_to :task
end
Then, the view:<%= fields_for :user, user do |f| %>
<%= f.fields_for :tasks do |builder| %>
<%= builder.text_field :name %>
<%= builder.fields_for :task_items do |ti| %>
<%= ti.check_box :completed %>
<% end %>
<% end %>
<% end %>
Related Topics
Why Slicing The Params Hash Poses a Security Issue on Mass-Assignment
Get All Products of Category and Child Categories (Rails, Awesome_Nested_Set)
How to Have Two Columns in One Table Point to The Same Column in Another with Activerecord
Rails3 Scope for Count of Children in Has_Many Relationship
Include Module in All Minitest Tests Like in Rspec
How to Add Usr/Local/Bin to Path Environment Variable on Ubuntu 12.0.4
Segmentation Fault with Ruby 1.8.7
Ruby-Open3.Popen3/How to Print The Output
How to Handle Omniauth Callbacks in Multiple Environments
Using a Self-Signed Certificate
Why Can't The Mail Block See My Variable
Make Headless Browser Stop Loading Page
Is It Still Advisable to Test Routes in Rails 4 with Minitest
Fresh Ruby Gem from Bundler - Cannot Load My Version.Rb File