Using form_for with Nested Resources

2019-03-01 07:05发布

问题:

I am building a To Do application in an attempt to get fluent with Rails. There are four levels of hierarchy in my app.

  1. User
  2. Goals (multiple goals per user)
  3. Tasks (multiple tasks per goal)
  4. Subtasks (multiple subtasks per task)

I have a working front end form for each of these that utilizes nested resources. My routes.rb has something like this

 resources :goal do
  resources :task do
    resources :subtask
  end
 end

What I would like to do now is that have all these forms right in one of the views of the user controller.

This the form that I have attempted to create :

<%= form_for @task, url: {controller: 'task', action: 'create'} do |f| %>

 <%= f.label :description %>

 <%= f.text_field :description %>

 <%= f.submit "Add Goal", class: "btn" %>

<% end %>

But I end up getting this error

No route matches {:action=>"create", :controller=>"task", :id=>"1"}

The :id=>1 corresponds to the user page I am on (http://localhost:3000/user/1)

What I understand is that there is that nowhere have I provided the goal_id for which this step is intended. No idea how to implement this.

Another thing that I have noticed is that a response to rake routes shows a lot of URI paths but nothing for POST method. It does not allow me to use a path from there in the url: in form_for because it does not match the POST method.

So my questions are :

  1. How to route a form_for that when you have nested resources?
  2. How to provide the ID of the parent resource while using form_for so that my create action is correctly routed?

回答1:

It is typically bad practice to nest routes beyond two levels deep. I would change your routes to:

  resources :goal do
    resources :task 
  end

and

  resources :task do
      resources :subtask
  end

Now if you run "bundle exec rake routes" in the command line you will see all of the nested routes and their corresponding helpers. Your current issue lies with the form_for method. You need to add the resource its nested with which in this case should be:

<%= form_for [@goal,@task] do |f| %>
    blah blah
<% end %>

Lastly, @goal is also still undefined so you'll need to define it in your 'new' action in the tasks controller. This is normally done by passing the id of the goal your task will be associated with via the params hash and the "link_to" used to get to the 'new' form. Then in the new action in your tasks controller:

@goal = Goal.find(params[:goal_id]) #The parameter can be named anything
@task = Task.new

Then in your 'create' action you should have the association made:

@task = Goal.tasks.new(task_params)
if @task.save
   flash[:success] = "Woot" 
   redirect_to somewhere_awesome_path
else
  whatever
end