Reputation: 4783
I'm trying to add a new model to an existing model mesh. The existing one works perfectly but I can't get the new one to work properly and am wondering if the association is able to work the way I'm trying to make it work. Update: As I just got asked: belongs_to through
was something I've read while gooling about the problem. If it doesn't exist, would has_one through
be the correct way? I tried it as well but it also didn't work.
Here is the existing mesh:
class Course
has_many :users, through: :enrollments
has_many :enrollments
end
class User
has_many :courses, through: :enrollments
has_many :enrollments
end
class Enrollment
belongs_to :course
belongs_to :user
# has fields :user_id, :course_id
end
Now a user should be able to rate a course he's completed. (If he has, there is an enrollment with his id and a course id.) I thought it would be best to write it as follows:
class Course
has_many :users, through: :enrollments
has_many :enrollments
has_many :ratings, through: :enrollments
end
class User
has_many :courses, through: :enrollments
has_many :enrollments
has_many :ratings, through: :enrollments
end
class Enrollment
belongs_to :course
belongs_to :user
has_one :rating
# has fields :user_id, :course_id
end
class Rating
belongs_to :enrollment
belongs_to :course, through: :enrollment
belongs_to :user, through: :enrollment
end
When I try to create a Rating in the console, I get the following error:
User.first.ratings.create(text: "test", course_id: Course.first.id)
ArgumentError: Unknown key: through
Update
When I use has_one through
insted, I get the following error:
ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection: Cannot modify association 'User#ratings' because the source reflection class 'Rating' is associated to 'Enrolment' via :has_one.
Is it possible to do it this way at all? Thanks!
Upvotes: 1
Views: 5384
Reputation: 555
class Course
has_many :users, through: :enrollments
has_many :enrollments
has_many :ratings, through: :enrollments
end
class User
has_many :courses, through: :enrollments
has_many :enrollments
has_many :ratings, through: :enrollments
end
class Enrollment
belongs_to :course
belongs_to :user
belongs_to :rating
# has fields :user_id, :course_id, rating_id
end
class Rating
has_one :enrollment
has_one :course, through: :enrollment
has_one :user, through: :enrollment
end
Note: Add foreignkey columns
And if you there is just one/two columns in ratings table merge them into enrollments like this.
class Course
has_many :users, through: :enrollments
has_many :enrollments
end
class User
has_many :courses, through: :enrollments
has_many :enrollments
end
class Enrollment
belongs_to :course
belongs_to :user
# has fields :user_id, :course_id, rating-columns...
end
Upvotes: 4
Reputation: 76784
Structure
Maybe you're complicating this too much
class Enrollment
belongs_to :course
belongs_to :user
end
This means you have a join model which stores unique records, referencing both course
and user
. Your ratings
are on a per user
and course
basis?
Why don't you just include rating
as an attribute of your enrolment
model?:
#enrolments
id | user_id | course_id | rating | created_at | updated_at
If you give rating
a numeric value (1 - 5), it will give you the ability to rate the different enrolments like this:
user = User.first
course = Course.first
user.enrolments.create(course: course, rating: 5)
--
Ratings
This is, of course, based on your current model structure.
If you want to include ratings
for courses
by users
(not tied to enrolment
), you may wish to use a join model
called course_ratings
or similar:
#app/models/user.rb
Class User < ActiveRecord::Base
has_many :enrolments
has_many :courses, through: :enrolments
has_many :ratings, through: :courses, class_name: "CourseRating"
end
#app/models/course.rb
Class Course < ActiveRecord::Base
has_many :enrolments
has_many :students, through: :enrolments, class_name: "User"
has_many :ratings, class_name: "CourseRating"
end
#app/models/course_rating.rb
Class CourseRating < ActiveRecord::Base
belongs_to :course
belongs_to :user
end
This will allow you to call:
user = User.first
user.courses.first.ratings
Upvotes: 2