How I solved a relational problem in Ruby On Rails

2 Min. Read
Aug 12, 2019

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.