How I solved a relational problem in Ruby On Rails
The Problem I encountered
There were three tables namely Student, Assignments and Courses. The tables Student and Course had many-to-many-relationship. For association the tables were joined using a join table namely “StudentCourses”. The concept of joining tables was fairly simple. Similarly, the tables Student and Assignment shared a one to many relation, where an assignment was assigned to several students. My code looked something like this:
student.rb
1
2
3
has_many :studentcourses
has_many : courses, through: :studentcourses
belongs_to :assignments
course.rb
1
2
has_many :studentcourses
has_many : students, through: :studentcourses
student_courses.rb
1
2
belongs_to :student
belongs_to :courses
assignment.rb
1
has_many :students
Things that I wanted to do
My main aim was to display the information from both course and assignment, along with the student information. Also while creating the student, the form would also take inputs that could save the course information. I achieved this through form nesting
form.slim
1
2
3
4
5
6
7
= form_for @student do |f|
= f.label :name
= f.text_field :name
= f.fields_for @course do |g|
= g.label :name
= g.select :id, Course.all.pluck(:name), {}, multiple: true
Problem faced
The problem I faced was that I kept trying to display the information but the assignment field in student was empty. To solve this what I has to do was I has to merge the assignment in the params of course attributes.
I had to merge the assigment attributes within the course attributes which I achieved using the following strategies.
Initially this was the code:
1
2
3
4
5
6
7
8
def new
student.courses.build
end
private
def student_params
p = params.require(:student).permit(:name, :roll, :age, courses_attributes: [:id])
end
How it was solved
The student_params
was converted to:
1
2
3
4
5
6
7
8
9
def student_params
p = params.require(:student).permit(:name, :roll, :age, courses_attributes: [:id])
new_param_with_assignment_id = p[:courses_attributes]. to_h. map do |x, y|
[x, y.merge({ assignment_id: current_assignment.id })]
end
p.merge(courses_attributes: (new_param_with_assignment_id_id.to_h))
end
So what’s happening in the code is, we’re trying to merge the assignment attributes within
the coourse attributes. The “courses_ attributes” was first converted to hash which would
then be created to a two-dimensional array. Creating this would lead to the variable x
pointing to y(i.e x => y
).
The assignment attributes was then converted to hash again which could be merged with another hash courses_attributes
.