How to construct a Rails for a many-to-many model? - ruby-on-rails

I have a User, a Wiki, and a Collaborator model:
class User < ActiveRecord::Base
has_many :wikis
has_many :collaborators
end
class Wiki < ActiveRecord::Base
has_many :wikis
has_many :collaborators
end
class Collaborator < ActiveRecord::Base
belongs_to :user
belongs_to :wiki
end
When I am editing a Wiki's collaborators I would like the form to look something like this:
My problem is that I cannot figure out how to construct the form. I thought the following would work but <% form_for :collaborator do |f|%> doesn't result in anything being included in the resulting page.
<% form_for :collaborator do |f|%>
<% possible_collaborators.each do |user| %>
<%= check_box_tag 'wiki[collaborator_ids][]', user.id, wiki.collaborators.include?(user) %>
<%= user.name %>
<br />
<% end %>
<%= f.submit %>
<% end %>

As you didn't post your controller code I can't be entierly sure what you are trying to accomplish, but I think you are missing out on the accepts_nested_attributes_for (docs, tutorial with controller code).
Furthermore I can only recommend you the use simple_forms or formtastic as those gems do a good job when it comes to complex forms and help you a lot with the basic use cases.

Related

Using has_many :through with fields_for and checkboxes to create associations

That title's a mouthful.
So I have something like this:
class Company < ActiveRecord::Base
has_many :company_partner_associations
has_many :partners, through: :company_partner_associations
end
class CompanyPartnerAssociation
belongs_to :company
belongs_to :partner
end
class Partner
has_many :company_partner_associations
has_many :companies, through: :company_partner_associations
end
And on a company form, i'm trying to make a list of all Partners with a checkbox next to them. If I check one, it creates association. If I uncheck it destroys.
<%= f.fields_for :company_partner_associations, Partner.all do |p| %>
<%= f.check_box :partner_id %>
<% end %>
fails because the object getting passed is a Partner, so getting undefined partner_id on Partner
I'm sure there's a nifty solution out there! Thank you!
Do this:
<%= f.collection_check_boxes :partner_ids, Partner.all, :id, :name %>
No fields_for.
This will have to be accompanied in the controller with the following params:
params.require(:company).permit(:company, :params, partner_ids: [])
This should set the partner_ids in your #company model.
With HABTM, you can declare associative data by populating the "collection_singular_ids" method; HMT has the same method appended with the has_many relation:
Although this will replace the current associated objects, it is much simpler than calling f.fields_for - especially for picking partners.
--
You can also use collection_check_boxes which is meant for this purpose :)
Not totally sure this is the problem here, but I think it could be that your controller is just not permitting the array of partner ids. So in your company_partner_params in your company controller needs to permit something like partner_attributes: [:id]. The syntax might not be perfectly correct there, but if that's something you're missing, you should look around for that.
This is what I think the form should look like:
<%= form_for #company do |f| %>
<%= f.fields_for :partners, Partner.all do |partner| %>
...
<% end %>
<% end %>

Is there a way to specify a different partial when using the nested_form gem?

Say I have the following polymorphic association:
class EmailAddress < ActiveRecord::Base
belongs_to :emailable, polymorphic: true
end
class Person < ActiveRecord::Base
has_many :email_addresses, as: :emailable, dependent: :destroy
accepts_nested_attributes_for :email_addresses
end
class Organization < ActiveRecord::Base
has_many :email_addresses, as: :emailable, dependent: :destroy
accepts_nested_attributes_for :email_addresses
end
I'm using the nested_form gem to help build a dynamic nested form where multiple email addresses can be added/removed when editing a person or organization. Ryan's documentation (modified for this example) states that if you use <%= f.fields_for :email_addresses %> then "it will look for a partial called "email_address_fields" and pass the form builder as an f variable to it."
This is handy, but in my case the email address fields partials would be exactly the same for both people and organizations. Instead of having both /app/views/people/_email_address_fields.html.erb and /app/views/organizations/_email_address_fields.html.erb, is there a way to tell the f.fields_for method to use a different partial (e.g., one created in /app/views/email_addresses/_fields.html/erb or something similar)?
I thought of the following:
<%= f.fields_for :email_addresses do |email_address_form| %>
<%= render partial: 'email_addresses/fields', locals: { f: email_address_form } %>
<% end %>
Is there a better way though?
No, I don't believe there is a better way, although you could make a helper method to remove duplication if you really want to. We use exactly the same strategy for nested link forms:
<%= f.fields_for :links do |link_form| %>
<%= render partial: "shared/link_fields", locals: { f: link_form } %>
<% end %>

has_many through form new method, no id, no elements

I've been hacking around with Rails 3.2.11 for a while, and am trying to do this the 'right' way.
I have three models (Reflection, Skill, Utilization) that relate to each other through has_many: through:
Utilization.rb
class Utilization < ActiveRecord::Base
attr_accessible :reflection, :skill, :used_skill #used_skill is a boolean
belongs_to :reflection
belongs_to :skill
end
Reflection.rb
class Reflection < ActiveRecord::Base
## attributes here ##
has_many :utilizations
has_many :skills, through: :utilizations
accepts_nested_attributes_for :utilizations
accepts_nested_attributes_for :skills
end
Skill.rb
class Skill < ActiveRecord::Base
## attributes here ##
has_many :utilizations
has_many :reflections, through: :utilizations
end
Within the app, skills are already defined. The user action I am trying to support is:
User gets form for new Reflection.
User sees a list of Skills and checks off which ones they have used (Utilization).
User posts to create new Reflection and create the associated Utilization objects.
Here is the new method reflection_controller.rb:
class ReflectionsController < ApplicationController
def new
#reflection = Reflection.new
Skill.all.each do |skill|
#reflection.utilizations.build(skill_id: skill.id, used_skill: false)
end
end
end
And an abbreviated _form.html.erb for Reflections
<%= form_for(#reflection) do |f| %>
<% f.fields_for :utilizations do |builder| %>
<%= builder.label :used_skill %>
<%= builder.check_box :used_skill %>
<%= builder.fields_for :skill do |skill| %>
<%= skill.label :description %>
<%= skill.text_field :description %>
<% end %>
<% end %>
<% end %>
So the problem is that even though there are multiple Skills and I .new the Utilization objects and associate them with the #reflection, they don't show up in the form. I've played with the data structures a little bit, and I can reach the point where in ReflectionController.new #reflection.utilizations contains Utilization objects, it still won't work; when I run #reflection.utilizations.count it returns 0. It looks like the problem is that since none of the objects have an id at that time, it simply will not render out in the form. But my understanding is that one should not create objects during the new method…
Is there something obvious I'm missing? Is there a better way to do this? I've seen examples, include Ryan Bates' Railscast where people just use code like:
def new
#survey = Survey.new
3.times do
question = #survey.questions.build
4.times { question.answers.build }
end
end
and supposedly this works fine.
I really appreciate the help. Trying to figure this out has been driving me crazy. This is my first question on SO, and I'm happy to add any clarifying data or additional code if you think it would help.
You forgot to use =:
<%#### Here ####%>
<%= f.fields_for :utilizations do |builder| %>
<%= builder.label :used_skill %>
<%= builder.check_box :used_skill %>
<%#### and here ####%>
<%= builder.fields_for :skill do |skill| %>

How do I save many-to-many relationships in a model

I have the following setup in my rails app:
A user registers and he chooses from a set of check boxes for Music Styles.
The Music Styles are only 4 right now but should be extendable. I'd like to have a list of MusicStyles that I can extend and change easily.
My approach would be to create a model 'MusicStyles' and a model 'UserMusicStyles' and then use a has_many_through association similar to:
class User < ActiveRecord::Base
has_many :user_music_styles
has_many :music_styles, :through => :user_music_styles
end
class UserMusicStyle < ActiveRecord::Base
belongs_to :user
belongs_to :music_style
end
class MusicStyle < ActiveRecord::Base
has_many :music_styles
has_many :users, :through => :user_music_styles
end
Now, during registration I would do something like MusicStyle.all.each do |m| ... to display the checkboxes but how do I save it to the database correctly in the user controller?
Any help much appreciated!
You can do it like this:
<%= form_for #user do |f| %>
<!-- User stuff -->
...
<% MusicStyle.all.each do |m| %>
<%= check_box_tag('user[music_style_ids][]', m.id, #user.music_styles.include?(m)) %>
<% end %>
<%= f.submit 'Save' %>
<% end %>

Nested Object w/ Checkboxes - mass-assignment even with accepts_nested_attributes_for?

I thought that there should have been a simple solution to this, given that Rails 2.3 has this newfangled nested forms feature. Basically I want to create or update a user and assign them roles at the same time.
It seems like I'm doing everything right but I get the error WARNING: Can't mass-assign these protected attributes: roles_attrributes.
I even tried changing the view to user[permissions_attrributes][role_id] because I thought that maybe the join table was confusing Rails.
Anyways, any suggestions on how this should actually work?
Model
class User < ActiveRecord::Base
has_many :permissions
has_many :roles, :through => :permissions
accepts_nested_attributes_for :roles
accepts_nested_attributes_for :permissions
end
Excerpt from view (notice I tried and failed to get fields_for to generate what I want here, maybe that's my problem?)
<% for role in Role.all %>
<%= check_box_tag( "user[roles_attrributes][id]",role.id) %>
<%= role.rolename %>
<br/>
<% end %>
Params coming across seem to be right:
{"user"=>{"password_confirmation"=>"[FILTERED]",
"roles_attrributes"=>{"id"=>"2"}, ...
Solution A combination of me misspelling, not using attr_accessible, needing to access permissions_attributes, and the form being slightly off.
Model:
has_many :permissions, :dependent => :destroy
has_many :roles, :through => :permissions
accepts_nested_attributes_for :permissions
attr_accessible :permissions_attributes
View:
<% Role.all(:order => "rolename ASC").each_with_index do |role,idx| %>
<%= check_box_tag( "user[permissions_attributes][#{idx}][role_id]",role.id) %>
<%= role.rolename %>
<br/>
<% end %>
If you correct the spelling of attributes in your check_box_tag, it looks like it should work.
<% for role in Role.all %>
<%= check_box_tag( "user[roles_attributes][id]",role.id) %>
<%= role.rolename %>
<br/>
<% end %>
it sounds like this attribute isn't marked as safe for updating. You should be able to fix it by adding the following to your model class:
attr_accessible :roles
or possibly:
attr_accessible :roles_attributes
If you look, you may already have an attr_accessible call you can add this to. For more information this is documented here:
http://api.rubyonrails.org/classes/ActiveRecord/Base.html#M002226

Resources