Bass
Bass

Reputation: 15

Creating custom URLs in Rails

I have 4 models. Departments, Courses, Sections, and Teacher All of them are heirachically connected with the proper belongs_to and has_many relations. A section belongs to a course which belongs to a department. A teacher has many sections, however, a course has many (is taught by many different) teachers.

I would like to construct the site in such a way that the URL /view/department_name/course_name/section_name/ shows the section with the name section_name, which belongs to the course named couse_name, which belongs to the department department_name.

However, I also want to /view/teacher_name/course_name/section_name to work the same way, where it shows all the sections associated with that teacher.

How can I do this?

FYI: the different models are connected by their IDs in the database. Section has a course_id integer associated with it, and course has a department_id integer. Section also has a department_id, which is redundant, but that's how I have it set up.

my routes.rb

Test2::Application.routes.draw do
  resources :courses
  resources :departments
  resources :teachers
  resources :sections
  resources :courses

Upvotes: 0

Views: 274

Answers (1)

PeterWong
PeterWong

Reputation: 16011

You mentioned things like section_name in your routes. You should make sure the names are all URL-safe.

Another point you have to consider is the two paths you want is in fact quite similar and thus you may need to handle both in the same controller action.

/view/department_name/course_name/section_name
/view/teacher_name/course_name/section_name

The only different part is the department_name vs teacher_name. Hopefully there is no any teacher have the same name with your department XD"

You may define the path as:

get '/view/:department_or_teacher_name/:course_name/:section_name' => 'some_controller#some_action'

Then in your action, you need something like this:

def some_action
  department_or_teacher = Department.find_by_name(params[:department_or_teacher_name]) || Teacher.find_by_name(params[:department_or_teacher_name])
  course = Course.find_by_name(params[:course_name])

  q = Section.where(name: params[:section_name]).where(course_id: course.id)
  if department_or_teacher.class.name == "Department"
    q = q.where(department_id: q.id)
  elsif department_or_teacher.class.name == "Teacher"
    q = q.where(teacher_id: q.id)
  end

  @section = q.first # I assume there should be only 1 section with a given name, under a specific course, and (under a specific department or a specific teacher)
end

The above generates 3 queries, which might not be very good. You may consider using joins:

def some_action
  department_or_teacher = Department.find_by_name(params[:department_or_teacher_name]) || Teacher.find_by_name(params[:department_or_teacher_name])

  q = Section.where(name: params[:section_name]).joins(:course).where("courses.name = ?", params[:course_name])

  if department_or_teacher.class.name == "Department"
    q = q.where(department_id: q.id)
  elsif department_or_teacher.class.name == "Teacher"
    q = q.where(teacher_id: q.id)
  end

  @section = q.first
end

There is still a difficulty in guessing the department or teacher. So if you redefine the path to something like this, the above could be cleaner:

get '/view/department/:department_name/:course_name/:section_name' => 'some_controller#some_action_for_department'
get '/view/teacher/:teacher_name/:course_name/:section_name' => 'some_controller#some_action_for_teacher'

def some_action_for_department
  @section = Section.where(name: params[:section_name])
                    .joins(:course).where("courses.name = ?", params[:course_name])
                    .joins(:department).where("departments.name = ?", params[:department_name]) # you mentioned there is a department_id in sections, thus you could actually setup a belongs_to :department in section
                    .first
end

def some_action_for_teacher
  @section = Section.where(name: params[:section_name])
                    .joins(:course).where("courses.name = ?", params[:course_name])
                    .joins(:teacher).where("teachers.name = ?", params[:teacher_name])
                    .first
end

This is the idea. I did not test it and just by guess. So please tell me if you encountered any problems.

Upvotes: 1

Related Questions