Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails nested resource parameters

I am newish to Ruby on Rails, using version 4.1 in a project.

I am a bit confused about the way the nested resources are working in rails. Perhaps someone can help.

I am building a tasking system as a learning project.

I have websites that have tasks that belong to them. When viewing tasks, I can do:

resources :websites do 
  resources :tasks
end

And a link will take me to my task just fine, with a url like http://myapp.com/websites/2/tasks/3

<%= link_to 'Show', website_task_path(website,task) %>

What I noticed though, is that I can change the website id in the url to anything - http://myapp.com/websites/what-the-hell-is-going-on-here/tasks/1 - works just the same as the other link. Or I can access that task with a different website id in the url as well.

So the question is, is Rails supposed to do anything with that piece of information by default? Is it up to me if I want to make sure that you are accessing the task with the right parent resource in the parameters?

like image 796
user3166452 Avatar asked Apr 10 '15 22:04

user3166452


1 Answers

Your task's ID is unique in the tasks table. As long as you want to show, edit or delete a task, this information is enough. You can easily get the parent through your association. But a nested resource allows you to create new tasks. In this case there is no ID set. You need to know the correct parent to set it on your task.

Excerpt from a possible TasksController:

class TasksController < ApplicationController

  before_action :set_website, only: [:create]

  def create
    @website.tasks.create!(task_params)
  end

  private

  def set_website
    @website = Website.find(params[:website_id])
  end

  def task_params
    params.require(:task).permit(:title, :text)
  end

end

Generated nested routes:

# Routes without :task_id - Parent matters!

website_tasks
GET  /websites/:website_id/tasks(.:format) tasks#index
POST /websites/:website_id/tasks(.:format) tasks#create

new_website_task
GET  /websites/:website_id/tasks/new(.:format) tasks#new

# Routes with :task_id - Parent redundant.

edit_website_task
GET  /websites/:website_id/tasks/:id/edit(.:format) tasks#edit

website_task
GET    /websites/:website_id/tasks/:id(.:format) tasks#show
PATCH  /websites/:website_id/tasks/:id(.:format) tasks#update
PUT    /websites/:website_id/tasks/:id(.:format) tasks#update
DELETE /websites/:website_id/tasks/:id(.:format) tasks#destroy

You can clean the routes from the redundant website_id by using shallow nesting. This is explained in detail in the Rails docs.

Basically it means:

resources :website do
  resources :tasks, only: [:index, :new, :create]
end
resources :tasks, only: [:show, :edit, :update, :destroy]

Which is the same as writing:

resources :websites do
  resources :tasks, shallow: true
end

There may be some use cases where the full path is valuable, e. g. you want to feed it to a search engine or you want the URL to be more speaking for readers.

like image 186
ToniTornado Avatar answered Oct 12 '22 08:10

ToniTornado