Reputation: 784
So if I have a many_to_many table with just the ids in it, and I want to reference it within another model, how would I do that?
So in the model, which is called Rating, I want to say that it is linked to both of those things in the association, in this case Course and Textbook. So a Rating is defined by the Textbook and the Course. Should I add two id attributes to Rating that link to both? Should I add an ID to the many_to_many and then have an ID attribute in the Rating table? Or is there something else I can do?
UPDATE:
So here is my ERD so far. You can ignore things with Students and Professors.
On the right hand side the the table that I made according to @RichPeck. I'm confused on how to create the entries. Lets say for example I create a Course.
course = Course.new(params)
I then do this to make a Textbook.
textbook = course.textbooks.build(params)
Now when I do course.textbooks
, it lists the textbook i just made but when I do textbook.courses
I get an empty list. Also, after saving them and exiting the shell then reopening, both commands reutnr empty list. Now this is because the join table, CourseTextboks, is empty. Why is rails not adding an entry into there? Do I need to do something special?
Here are the relevant files:
CreateCourseTextbooksMigration
class CreateCourseTextbooks < ActiveRecord::Migration
def change
create_table :course_textbooks do |t|
t.references :course, index: true
t.references :textbook, index: true
t.timestamps
end
end
end
CreateTextbookMigration
class CreateTextbooks < ActiveRecord::Migration
def change
create_table :textbooks do |t|
t.string :title
t.string :authors
t.string :edition
t.float :price
t.string :isbn
t.text :description
t.string :image_url
t.date :published
t.timestamps
end
end
end
CreateCourseMigration
class CreateCourses < ActiveRecord::Migration
def change
create_table :courses do |t|
t.string :title
t.string :letters
t.integer :number
t.timestamps
end
end
end
And the models:
Course
class Course < ActiveRecord::Base
has_many :student_courses
has_many :students, through: :student_courses
has_many :professor_courses
has_many :professors, through: :professor_courses
has_many :textbook_courses
has_many :textbooks, through: :textbook_courses
validates_presence_of :title, :letters, :number
end
Textbook
class Textbook < ActiveRecord::Base
has_many :textbook_courses
has_many :courses, through: :textbook_courses
end
CourseTextbook
class CourseTextbook < ActiveRecord::Base
belongs_to :course
belongs_to :textbook
has_many :rating_course_textbooks, :class_name => "RatingCourseTextbook"
has_many :ratings, :through => :rating_course_textbooks
validates_presence_of :course
end
Can you help me out, guys?
UPDATE2:
I figured it out after about a half hour of searching and paying more attention to the SQL generated when creating objects.. So when you do a course.build
it just returns the object and somehow links it, but not through join table. I found out that you have to save the course NOT the new professor, or just use create.
This is what was happening
>>> course = Course.create(params)
# INSERT into courses table
>>> professor = course.build(params)
>>> professor.save
# INSERT into professors table
>>> course.professors
# [Professor {id:1, blah}]
>>> professor.courses
# []
And this is what works now
>>> course = Course.create(params)
# INSERT into courses table
>>> professor = course.build(params)
>>> course.save
# INSERT into professors table
# INSERT into professor_courses table
>>> course.professors
# [Professor {id:1, blah}]
>>> professor.courses
# [Course {id:1, blah}]
YAY! Now just to see if it all works with my other models, like the RatingsCourseTextbooks
UPDATE3 So after messing around for a little while, I discovered that I was making it too complicated. A rating can only have one CourseTextbook so I didn't need another join table. I just added a CourseTextbook ID to Rating.
Thanks for your support
Upvotes: 0
Views: 476
Reputation: 76774
I'm guessing you're new to Rails, so I'll explain how this would work for you here:
One of Rails' core features is the ActiveRecord association
ActiveRecord brings the relational database schema to life, allowing you to reference different "related" models from a single query. It's how you can call @user.images
etc
ActiveRecord sits above the database layer, and is referenced by your models using the has_many
, belongs_to
, etc references. If you want to reference your related models, you'll have to use these, like this:
#app/models/user.rb
Class User < ActiveRecord::Base
has_many :images
end
#app/models/image.rb
Class Image < ActiveRecord::Base
belongs_to :user
end
Your Code
Seems you're using has-and-belongs-to-many
As mentioned by Sachin Singh, you'll likely be best using a polymorphic
relationship with these models:
This allows you to reference the relationship from multiple models, with Rails / ActiveRecord populating the polymorphic fields with the model name & id. Here's how your models may look:
Class Rating < ActiveRecord::Base
belongs_to :rateable, :polymorphic => true
end
Class Course < ActiveRecord::Base
has_many :ratings, :as => :rateable
end
Class Textbook < ActiveRecord::Base
has_many :ratings, :as => :rateable
end
Update
If you wanted to have specific textbooks for certain courses, and have ratings for those textbooks, you'll probably best use has_many :through
This might be completely off the mark, but I'd set up the courses
and textbooks
tables as "top-level" tables, and then have textbook_courses
as a join-model. I'd then have textbook_course_ratings
as another join model to give the required ratings per textbook
The goal will be @course.textbooks.ratings
Class Course < ActiveRecord::Base
has_many :textbook_courses
has_many :textbooks, :through => :textbook_courses
end
Class Textbook < ActiveRecord::Base
has_many :textbook_courses
has_many :courses, :through :textbook_courses
end
Class TextbookCourse < ActiveRecord::Base
belongs_to :textbook
belongs_to :course
has_many :textbook_course_ratings, :class_name => "TextbookCourseRating"
has_many :ratings, :through => :textbook_course_ratings
end
Class TextbookCourseRating < ActiveRecord::Base
belongs_to :textbook_course
belongs_to :rating
end
Class Rating < ActiveRecord::Base
has_many :textbook_course_ratings, :class_name => "TextbookCourseRating"
has_many :textbooks, :through => :textbook_ratings
end
I've never done such a deep has_many :through
relationship before; but it should work
There several important things to note:
primary key
(id) - HABTM doesn't need them, but HMT doesaccepts_nested_attributes_for
to do :)Your join tables for this would look like:
textbook_courses
id | course_id | textbook_id | created_at | updated_at
textbook_courses_ratings
id | textbook_course_id | rating_id | created_at | updated_at
Upvotes: 2
Reputation: 7225
if, A Course and TextBook has many ratings, then ratings table should be polymorphic.
class Rating < AcrtiveRecord::Base
belongs_to :ratable, polymorphic: true
end
class Course < ActiveRecord::Base
has_many :ratings, as: :ratable
end
class TextBook < ActiveRecord::Base
has_many :ratings, as: :ratable
end
ratings table will have two column named ratable_type and ratable_id
ratable_type : it will hold class name Course or Textbook.
ratable_id : it will hold id of associated abject.
Upvotes: 0