Here's my model set up.
Band Model
has_many :bands_genres
has_many :genres, :through => :bands_genres
Genre Model
has_many :bands_genres
has_many :bands, :through => :bands_genres
BandsGenre Model
belongs_to :band
belongs_to :genre
I have a form where you can add a new band and then select a genre from a dropdown field that pulls from the pre-set genres in the genre model.
So what I ultimately need to do is set up a form so that when a band adds their band and select a genre, it creates the correct join in the bands_genre model.
Not sure where to start with setting up the form, controllers and models for this.
I'm running Rails 3.0.3
There are quite a few text/video casts covering this, since its a popular use case. I would encourage you to look at:
http://railscasts.com/episodes/73-complex-forms-part-1 or its equivalent asciicast (which is a text based cast of the video).
Further I would recommend you use formtastic. Associations are managed automatically so it makes form building trivial and keeps your code tidy. And yes there are casts for that too.
http://railscasts.com/episodes/184-formtastic-part-1
Edit:
Band Model
has_many :genres, :as => :band_genres
Genre Model
has_many :bands, :as => :band_genres
Your genre table has a band_id, and your band table has a genre_id.
bands_controller
def new
#genres = Genre.all
#post = Post.new
end
posts/new.html.haml
(This part I'm a little unsure of, but it roughly goes like this)
- form_for #post do |f|
= f.select :genre_id, #genres, {}
= f.submit
Related
I have the following models:
class Product < ActiveRecord::Base
has_many :product_recommendation_sets, :dependent => :destroy
has_many :recommendation_sets, :through => :product_recommendation_sets
end
class RecommendationSet < ActiveRecord::Base
has_many :product_recommendation_sets, :dependent => :destroy
has_many :products, :through => :product_recommendation_sets
has_many :recommendations
end
class Recommendation < ActiveRecord::Base
belongs_to :recommendation_set
end
And am adding recommendations recommendations_set like so:
p = Product.find_by_wmt_id(product) || Product.create( ItemData.get_product_data(product) )
recommendation = find_by_rec_id(rec_id) || create( ItemData.get_product_data(rec_id) )
rec_set = RecommendationSet.find_or_create_by_rating_set_id_and_model_version_and_product_id(rating_set.id, model_version, p.id)
sec_set.update_attributes(
:rating_set_id => rating_set.id,
:product_id => p.id,
:model_version => model_version,
:notes => note
)
sec_set.recommendations << recommendation
sec_set.save
prs = ProductRecommendationSet.find_or_create_by_recommendation_set_id_and_rating_set_id_and_product_id(rec_set .id, rating_set.id, p.id,)
prs.update_attributes(
:recommendation_set_id => rec_set.id,
:rating_set_id => rating_set.id,
:product_id => p.id
)
This works as expected, however my problem is that I have multiple recommendation_sets which belong to multiple products, and each of the recommendation_sets may have the same recommendation. By saving each recommendation to a recommendation_set as I am currently doing, if two recommendation_sets have the same recommendation, only one of the sets will add that recommendation. Is there anyway of saving each recommendation to multiple recommendation_sets using a secondary id, such as save by recommendation_id_and_product_id, or would I need to change this releationship to a has_many :through?
Based on your clarification, I think you basically have a many-to-many relationship between RecommendationSet and Recommendation. Presently, you have a one-to-many.
There are a couple of options:
Use the has_and_belongs_to_many method in both models to describe the relationship;
Manually create a "join" model and then give both RecommendationSet and Recommendation a has_many to this join model (with two corresponding belongs_to lines in the join model pointing to the other two models);
A has_many ... :through style, like you mentioned
Note that the first two options require you to have a join table.
If you require additional information on the join table/model, I tend to go with the 2nd option. Otherwise, either the first or third are perfectly valid.
Ryan Bates of RailsCasts made an episode about this here: http://railscasts.com/episodes/47-two-many-to-many
And some more information from the Rails documentation: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Many-to-many
In short, if you don't need extra info on the join, I think your idea of the has_many ... :through is perfectly fine.
Let me know whether that helps
So I need advice on how to set up a rails form in a many-to-many relationship where I save extra information in the join model.
Background: I have two models, User and Skill. A user can have many skills; skills can be mapped to many users. I have a join model SkillAssessment to save this, which saves a user_id, skill_id. I want to be able to save in the join model how good a user is at a skill, or skill_level. This can take integer values from 0-5.
The Skills table is prepopulated with entries, so when creating a new user I would like for the "Skills" input to be a bunch of checkboxes. I don't know how to add a field beside the checkboxes where a user can input how good he/she is at a skill. This ideally would be a drop-down, but that isn't super important.
Here is a picture of how it might look: the checkbox is on the left, followed by a label, followed by an extra field (text input, drop down.. anything) to rate the skill level.
I have these models working great once the data is saved, but I don't know how to alter the form. If it were just saving skills, I would put:
= f.input :skills, :label => _("Skills"), :as => :check_boxes, :collection => Skill.all
How can I achieve what I'm going for here? I thought maybe semantic_fields_for or something?
FYI I use slim and formtastic, but know haml and plain rails so advice in any format is much appreciated! Thanks guys
You've said everything works great once its in the database, so I'm assuming that you've set up your model as a :has_many, :through rather than a has_and_belongs_to_many. If not, you'll need to do that to save extra attributes to your join model.
If you haven't already done so, you're going to need to turn on accepts_nested_attributes_for SkillsAssessments in your User model.
If you're using attr_accessible you'll also need to add skill_assessments_attributes to your user list of accessible attributes.
End result is that your user model would look like so:
models/user.rb
===============
class User < ActiveRecord::Base
has_many :skill_assessments
has_many :skills, :through => :skill_assessments
accepts_nested_attributes_for :skill_assessments
attr_accessible :user_field, ..., :skill_assessments_attributes
Again, if your using attr_accessible, you'll need to ensure that all three fields on your skill_assessment model are accessible.
models/skill_assessment.rb
===========================
class SkillAssessment < ActiveRecord::Base
belongs_to :user
belongs_to :skill
attr_accessible :user_id, :skill_id, :skill_level
Then in your view, you're going to have something like this (haml):
views/users/_form.html.haml
============================
= semantic_form_for #user do |f|
= f.input :user_field_1
...
= f.input :user_field_n
= f.semantic_fields_for :skill_assessments do |sa|
= sa.input :skill_id, :label => "Skill", :collection => Skill.all
= sa.input :skill_level, :label => "Proficiency", :collection => [1,2,3,4,5]
= f.submit
I have previously used has_and_belongs_to_many associations in my older Rails apps but am moving to using has_many, :through for new and current apps. However, I believe I am missing something central to the has_many, :through association in Rails 3.
Currently, I am building an app for our town's volunteer fire department. When we have a meeting, we want to check off whether or not a fire fighter is present, excused, or absent.
My models:
#FireFighter Model
class FireFighter < ActiveRecord::Base
has_many :attendance_meetings
has_many :meetings, :through => :attendance_meetings
accepts_nested_attributes_for :meeting
#Meeting Model
class Meeting < ActiveRecord::Base
has_many :attendance_meetings
has_many :fire_fighters, :through => :attendance_meetings
accepts_nested_attributes_for :fire_fighter
#AttendanceMeeting Model
class AttendanceMeeting < ActiveRecord::Base
attr_accessor :status # this is the added property on the join model that we need populated
belongs_to :fire_fighter
belongs_to :meeting
The problem I'm having is how to set this up in the view. Currently, I have:
= hidden_field_tag "meeting[fire_fighter_ids][]"
-#meeting.fire_fighters.each do |fire_fighter|
= fire_fighter.fire_fighter_name
%span.radio_btn= radio_button_tag :fire_figher_ids, fire_fighter.id, "present"
present
%span.radio_btn= radio_button_tag :fire_figher_ids, fire_fighter.id, "excused"
excused
%span.radio_btn= radio_button_tag :fire_figher_ids, fire_fighter.id, "absent"
absent
%br
This will spit out each fire fighter and three radio buttons for each fire fighter (with options for present, excused, or absent). However, the name of the resulting radio buttons are all the same, so you can only pick one for all fire fighters.
As I noted above, I feel certain I am missing something basic but I am stumped. I've read through tons of SO questions and The Rails 3 Way book's sections on ActiveRecord. Any suggestions or directions would be most appreciated. Thank you!
It should be something like:
class Meeting < ActiveRecord::Base
has_many :attendance_meetings
has_many :fire_fighters, :through => :attendance_meetings
accepts_nested_attributes_for :attendance_meetings
# view
= form_for #meeting do |f|
= f.fields_for :attendance_meetings do |f_a_m|
= f_a_m.object.fire_fighter.name
= f_a_m.check_box :status, 'present'
= f_a_m.check_box :status, 'excused'
= f_a_m.check_box :status, 'absent'
For the approach you're taking you need to build the association for each firefighter with the meeting. Something like:
#meeting.firefighters << Firefighter.all
This doesn't seem particularly optimal however. I would form the join with a boolean :status for those who are absent (t) or excused (f) with present not included, imagine it like meeting_absentees. Seems backward but in this way your table will have far fewer rows.
I've got my models setup for a many-to-many relationship:
class Workshop < ActiveRecord::Base
has_many :workshop_students
has_many :students, :through => :student_workshops
accepts_nested_attributes_for :students
end
class Student < ActiveRecord::Base
has_many :student_workshops
has_many :workshops, :through => :student_workshops
accepts_nested_attributes_for :products
end
class StudentWorkshop < ActiveRecord::Base
belongs_to :student
belongs_to :workshop
end
As you can see above, a student can have many workshops and workshop can have many students.
I've looked at the following Rails casts: here and here. And most of the online sources I stumble across only show how to do nested forms for creating new objects within the parent form.
I don't want to create a new object. I only want to add an existing object to the parent form. So for example. If I decide to create a new workshop, I'd like to assign existing students to the workshop.
One thing I don't understand is, how do I link students into the workshop form? Second, when the params are passed, what should be in the controller method for update/create?
If anyone can point me to the right direction, I would appreciate it.
The easiest thing to do is:
<%= f.collection_select(:student_ids, Student.all, :id, :name, {:include_blank => true}, {:selected => #workshop.student_ids, :multiple => true} )%>
You should not have to do anything in the create action.
Ok, for anyone coming across the same issue in the future. The solution I came up with was in def create. I am able to access a POST attribute called student_ids, which comes in the form of an array
I'm working on a add to cart form. It looks something like this
#Models
Order.rb
has_many :line_items
accepts_nested_attributes_for :line_items, :allow_destroy => true
LineItem.rb
has_one :product
belongs_to :order
Product.rb
belongs_to :line_item
I'd like to create a form in product#show to allow multiple related products to be added to the order/cart at once, basically create or update a line item for each product.
Probably something like this in the view (HAML to keep it brief).
-form_for #order do |f|
- if has_related?
- for related in #products.related_products
- f.field_for :line_item do |li_form|
= li_form.text_field :quantity
= li_form.hidden_field :product_id
= related.product_name
What would it take to actually make something like this work?
I would need more info to be sure, but it seems that a LineItem belongs_to :product and Product should NOT belong_to :line_item unless there really is a 1-1 relationship there (which wouldn't make sense to me, and doesn't follow the normal convention of these sorts of systems)
Note** using - before form_for and fields_for has been deprecated in rails 3 in favor of = since the form does actually render html
= fields_for :line_items do |li_form| is the syntax for a has_many relationship
The rest all depends on your user experience design.
Hope this helps!