I have gone through tons of the form_for nested resource questions and can't get any of the solutions to work for me. I figured its time to ask a personalized question.
I have two models, jobs and questions, jobs has_many questions and questions belong_to jobs.
I used scaffolding to create the controllers and models then nested the resources in the routes.rb.
root :to => "pages#home"
resources :jobs do
resources :questions
end
get "pages/home"
get "pages/about"
get "pages/contact"
class Job < ActiveRecord::Base
has_many :questions
end
class Question < ActiveRecord::Base
belongs_to :job
end
Right now I am trying to access '/jobs/1/questions/new' and keep getting the
NoMethodError in Questions#new
I started with the error No route matches {:controller=>"questions"} when the code was
<%= form_for(#question) do |f| %>
I know this is wrong, so I started to try other combos and none of them worked.
I've tried
<%= form_for([#job.questions.build ]) do |f| %>
that
<%= form_for([#job, #job.questions.build ]) do |f| %>
that
<%= form_for(#job, #question) do |f| %>
Among a bunch of other combinations and that are not working.
Here is a link to my rake routes : git clone https://gist.github.com/1032734
Any help is appreciated and let me know if you need more info, thanks.
I just pass the URL as an extra option:
<%= form_for(#question, :url => job_questions_path(#job)) do %>
EDIT:
Also try:
form_for([#job, #question])
This is how I solved mine :)
In your questions/_form.html.erb
<%= form_for [#job, #question] do %>
For this to work, you need the job's id. You'll pass it as follows:
In the questions_controller.rb
def new
#job = Job.find(params[job_id])
#question = #job.questions.build
end
Build(.build) is similar to using new(.new) in the code above, with differences only in older versions of rails; rails 2 down.
Now for the create action (still in questions_controller.rb)
def create
#job = Job.find(params[:job_id])
#question = #job.questions.build(question_params)
end
If you only use these, the job_id and user_id field in the question model will be empty. To add the ids, do this:
In your questions_controller.rb add job_id to job_params like so:
def question_params
params.require(:question).permit(:ahaa, :ohoo, :job_id)
end
Then to pass the user's id (if you are using Devise), do:
def create
#job = Job.find(params[:job_id])
#question = #job.questions.build(question_params)
#question.user_id = current_user.id
end
Related
I'm following the Rails tutorial and making changes where appropriate, with the intention that my tutorial project will become a full-fledged production app after the completion of the tutorial.
I've run into a snag with the second model portion of the tutorial. Here is how I've written my second model.
In my policy.rb:
class Policy < ApplicationRecord
has_one :insured
end
In my insured.rb:
class Insured < ApplicationRecord
belongs_to :policy
end
In my routes.rb:
resources :policies do
resource :insured
end
In my insureds_controller.rb:
def create
#policy = Policy.find(params[:policy_id])
# next line is raising the error
#insured = #policy.insured.create(insured_params)
redirect_to #insured
end
private
def insured_params
params.permit(:name, :address, :phone, :email)
end
I've inspected the #policy object with render plain: #policy.inspect and can confirm that ActiveRecord is retrieving the policy correctly. When I inspect the attributes of #policy, using render plain: #policy.attribute_names.inspect, I don't see an insured attribute, which I thought Rails was supposed to automatically manage for me. In the tutorial, an article has_many :comments, and a comment is supposedly easily created and associated with the parent article with this call: #article.comments.create(comment_params). I also noticed that the tutorial uses params.require(:comment).permit(...) while I have to use params.permit(...), after inspecting the params hash I saw that the :insured attributes existed in the top-level of the hash, instead of being tied to an :insured key within the hash.
I tried manually saving and assigning the #insured object like so:
def create
#policy = Policy.find(params[:policy_id])
#insured = Insured.new(insured_params)
if #insured.save
#policy.insured = #insured
redirect_to #insured
end
end
Only to run into the following error in my .../insureds/new.html.erb:
<h1>New Insured</h1>
<h1><%= #policy.policy_number %></h2>
<%= render 'form' %>
<%= link_to 'Cancel', policy_path(#policy) %>
Which derives from my partial form .../insureds/_form.html.erb:
# the following line raises the error
<%= form_with model: #insured, local: true do |form| %>
# html omitted for brevity
<% end %>
Error: 'undefined method insureds_path'. This is weird because when I inspect the HTML I can see the form action for this view is /policies/[:id]/insured.
Sorry for the massive wall of text, I wanted to show you guys that I did try to figure out what is going wrong.
There is an error in your config/routes.rb file:
resources :policies do
# change it for:
collection do
get 'insured', to: 'policies#show_insured', as: 'show_policy_insured'
# maybe unnecessary to be here
# get 'insured/new', to: 'insureds#new', as: 'new_policy_insured'
# post 'insured/create', to: 'insureds#create', as: 'create_policy_insured'
# delete 'insured/delete', to: 'insureds#delete', as: 'delete_policy_insured'
end
end
# add resources here
resources :insureds
In policy_controller.rb:
def show_insured # 'policy/:id/insureds/
end
In insureds_controller.rb:
def show # '/insureds/:id'
end
def create
...
redirect_to show_policy_insured && return if #insured_policy
end
# before_filter or before_action
#policy = Policy.find(params[:id])
#insured_policy = #policy.insured
Check it and run this to see your routes:
$ bundle exec rake routes
get /policies/:id/insured => 'policies_controller#show_insured'
get /insureds/:id => 'insureds_controller#show'
get /insured/new => 'insureds_controller#new'
post /insureds/create => 'insureds_controller#create'
delete /insureds/:id/delete => 'insureds_controller#delete'
#maguri, that's not all necessary. The stumbling block I was running into was that Rails couldn't automatically determine the correct routes. When I provided my own urls in the form_with declarations, everything went smoothly.
Observe the following change in my _form.html.erb for the Insured model, which belongs_to Policy, which has_one Insured.
<%= form_with model: #insured, url: policy_insured_path(#policy) local: true do |form| %>
In my updated insureds_controller.rb file, using #Phlip's suggestion:
def create
#policy = Policy.find(params[:policy_id])
#insured = #policy.create_insured(insured_params)
if #policy.insured.save
redirect_to policy_insured_path(params[:policy_id])
else
render 'new'
end
end
This allows me to keep routes.rb clean and simple:
resources :policies do
resource: insured
end
Thank you for your answer, it helped me discover the problem was with my routes.
In my rails app I'm trying to use a form partial to display the same quiz on the new and edit views. I can see the new view page, but when I hit <%= f.submit "Submit Answers" %> I get an error saying No route matches [POST] "/flex_quiz/new".
Here is the form for line in my partial:
<%= form_for #flex_quiz, url: url do |f| %>
And here's how the locals stand in my new view:
<%= render partial: "quiz", locals: { url: new_flex_quiz_path, method: :post } %>
And my edit view:
<%= render "quiz", url: edit_flex_quiz_path(#flex_quiz), method: :put %>
Here are the route paths:
Prefix Verb URI Pattern Controller#Action
...
flex_quiz_index GET /flex_quiz(.:format) flex_quiz#index
POST /flex_quiz(.:format) flex_quiz#create
new_flex_quiz GET /flex_quiz/new(.:format) flex_quiz#new
edit_flex_quiz GET /flex_quiz/:id/edit(.:format) flex_quiz#edit
flex_quiz GET /flex_quiz/:id(.:format) flex_quiz#show
PATCH /flex_quiz/:id(.:format) flex_quiz#update
PUT /flex_quiz/:id(.:format) flex_quiz#update
DELETE /flex_quiz/:id(.:format) flex_quiz#destroy
Can anyone suggest how to fix this? I have looked at several similar posts (like this and this) but since I'm using partials the solution here is going to have to be a bit different.
EDIT
Here are my definitions in my flex_quiz_controller:
class FlexQuizController < ApplicationController
before_action :require_sign_in
def show
#flex_quiz = FlexQuiz.find(params[:id])
end
def new
#flex_quiz = current_user.build_flex_quiz
end
def create
#flex_quiz = FlexQuiz.new
#flex_quiz.flex01 = params[:flex_quiz][:flex01]
#flex_quiz.flex02 = params[:flex_quiz][:flex02]
#flex_quiz.flex03 = params[:flex_quiz][:flex03]
#flex_quiz.flex04 = params[:flex_quiz][:flex04]
#flex_quiz.flex05 = params[:flex_quiz][:flex05]
#flex_quiz.flex06 = params[:flex_quiz][:flex06]
#flex_quiz.flex07 = params[:flex_quiz][:flex07]
#flex_quiz.flex08 = params[:flex_quiz][:flex08]
#flex_quiz.flex09 = params[:flex_quiz][:flex09]
#flex_quiz.flex10 = params[:flex_quiz][:flex10]
#flex_quiz.user = current_user
if #flex_quiz.save
flash[:notice] = "Quiz results saved successfully."
redirect_to user_path(current_user)
else
flash[:alert] = "Sorry, your quiz results failed to save."
redirect_to welcome_index_path
end
end
def edit
#flex_quiz = FlexQuiz.find(params[:id])
end
def update
#flex_quiz = FlexQuiz.find(params[:id])
#flex_quiz.assign_attributes(flex_quiz_params)
if #flex_quiz.save
flash[:notice] = "Post was updated successfully."
redirect_to user_path(current_user)
else
flash.now[:alert] = "There was an error saving the post. Please try again."
redirect_to welcome_index_path
end
end
private
def flex_quiz_params
params.require(:flex_quiz).permit(:flex01, :flex02, :flex03, :flex04, :flex05, :flex06, :flex07, :flex08, :flex09, :flex10)
end
end
If you want to create new flex_quiz objects, then you're going to want to POST to flex_quiz_index_path.
Notice in your route paths, if you look at new_flex_quiz, the HTTP verb is a GET.
It may be slightly unintuitive, but the new action is actually a GET request.
The action in which the object is supposed to be created in is the create action.
So to solve your problem this should do the trick:
<%= render partial: "quiz", locals: { url: flex_quiz_index_path, method: :post } %>
EDIT:
Instead of defining locals, you can simply define your forms in form_for as such:
You will also have to define #flex_quiz in your controller actions as well (in your case new and edit) form_for will automatically infer the appropriate URL.
From documentation:
However, further simplification is possible if the record passed to
form_for is a resource, i.e. it corresponds to a set of RESTful
routes, e.g. defined using the resources method in config/routes.rb.
In this case Rails will simply infer the appropriate URL from the
record itself.
You'll also need to change the naming from singular form to plural.
The rails to do resuable forms is:
app/views/flex_quiz/_form.html.erb:
<%= form_for(#flex_quiz) do |f| %>
# ...
<% end %>
app/views/flex_quiz/new.erb:
<h1>Create a new quiz</h1>
<%= render 'form' %>
app/views/flex_quiz/edit.erb:
<h1>Edit a quiz</h1>
<%= render 'form' %>
While using locals can often be a good idea its not needed here. Note we just pass the resource and not a URL to form_for - that is convention over configuration in action and is what makes Rails awesome.
Rails figures out all by itself what URL to use for the action attribute and what method to use based on if the resource has been saved.
However for this to work you to actually follow the conventions. Make sure you are using the proper plural forms (the plural of quiz is quizzes):
resources :flex_quizzes
class FlexQuizzesController < ApplicationController
end
Unfortunately when it comes to rest of your setup you need to revisit the drawing board. Its not very realistic to think that you can do this with a single model. You would usually have several models with relations:
class Quiz
has_many :questions
end
class Question
belongs_to :quiz
has_many :answers
end
class Answer
belongs_to :question
end
class UserQuiz
belongs_to :user
belongs_to :quiz
end
class UserAnswer
belongs_to :question
belongs_to :answer
end
You would use one or several controllers to let admins create the quizes and a separate controller to let users answer the quiz. Its a quite common domain so you should be able to find plenty of examples.
I'm probably doing something stupid, but...
app/model/user.rb:
class User < ActiveRecord::Base
has_one :totem
config/routes.rb:
resources :users do
resource :totem
end
app/controllers/totems_controller.rb:
class TotemsController < ApplicationController
before_filter do
#user = User.find(params[:user_id])
end
def new
#totem = #user.build_totem
end
end
app/views/totems/new.html.erb:
<%= form_for [#user, #totem] do |f| %>
<% end %>
Then, when I navigate to /users/123/totem/new, I get the error:
ActionView::Template::Error (undefined method `user_totems_path' for #<#<Class:0x007f9d3c843b00>:0x007f9d3bb6dd68>):
But because I'm using resource :totem instead of resources :totems in routes.rb, the path helper it should be using is user_totem_path. Why isn't it trying to use the correct path helper?
Found my answer in another question: Ruby on rails: singular resource and form_for
app/models/totem.rb:
class Totem < ActiveRecord::Base
model_name.instance_variable_set :#route_key, 'totem'
belongs_to :user
end
(not sure why this Q&A didn't appear in my earlier searches...)
Or you could just use
form_for #totem, :url => user_totem_path(#user) do |f|
instead of
resource :totem
it should be
resources :totem
I couldn't figure out why too. I used this way (providing a url to form_for) to bypass the problem
<%= form_for [#user, #totem], :as => :totem, :url => user_totem_path do |f| %>
Also, some research from google find out that there's a bug report in earlier rails. But i am not sure whether it's been fixed or not in the latest rails. Here is the link if you would like to do more research
https://rails.lighthouseapp.com/projects/8994/tickets/267
I am trying to get a basic form to work and am struggling because I keep getting the error
undefined method `profiles_index_path' for #<#<Class:0x4fe1ba8>:0x4fccda0>
I have checked through and can't seem to work out where I am going wrong.
In my view (new.html.erb) I have:
<%= form_for #profile do |f| %>
<%= f.text_field :name %>
<%= f.text_field :city %>
<%= f.text_field :country %>
<%= f.text_field :about %>
<%= f.submit "Create Profile" %>
<% end %>
In my profiles controller I have:
class ProfilesController < ApplicationController
def new
#title = "New Profile"
#profile = Profiles.new
end
def create
#user = current_user
#profile = #user.profiles.new(params[:profile])
if #profile.save
redirect_to profile_path, :notice => "Welcome to your new profile!"
else
render "profiles#new"
end
end
def edit
#user = current_user
#profile = #user.profiles.find(params[:id])
end
def update
#title = "Update Profile"
#user = current_user
#profile = #user.profiles.find(params[:id])
if #profile.update_attributes(params[:profile])
redirect_to profile_path
else
render action: "edit"
end
end
def index
#user = current_user
#profile = #user.profiles.all
#title = "Profile"
end
end
And finally in my profiles model I have
class Profiles < ActiveRecord::Base
belongs_to :user
end
Any help people can offer really would be much appreciated because I am stumped. :)
Sorry forgot to include routes:
controller :profiles do
get "newprofile" => "profiles#new"
get "updateprofile" => "profiles#update"
get "profile" => "profiles#home"
end
resources :profiles, :controller => 'profiles'
The problem is indeed the way you've pluralized your model name. Don't do that. It should be a Profile, not a Profiles. There my be some work around to allow you to use a plural model name, but the answer is to stick to Rails convention rather than fighting the framework. Rename your model to Profile and the url_for helpers will understand how to correctly turn a new Profile object into a /profiles URL.
If you run "rake routes" command, do "profiles_index" appear in your routes? Usually for the index page of a model, the work 'index' is left out so the route is profiles_path
You error probably comes from a view where you've used profiles_index_path instead of profiles_path
I think it's failing due to the convention not being followed with your model name.
So I think you're problem is mostly around that you aren't following the convention on the model name, which would classically be singular, since each instance represents one profile. I think the form_for helper is trying to figure out what to do with it and failing as a result. So you have two options to try and resolve. Refactor the model name to singular (I'm not clear exacly how difficult that would be) or pass the :url paramater to form_for so it knows where to post to.
<% form_for #profile, :url => path_to_create_action do |f| %>
more information here:
I'm working with Rails 5 and I got the same error and it was specific using the word Media as my model and RoR used Medium as the plural so I got different routes when executing rake routes.
What I did to fix it was:
Delete the model I just have created.
rails d scaffold Media
Edit config/initializers/inflections.rb with:
ActiveSupport::Inflector.inflections(:en) do |inflect|
# Here you can put the singular and plural form you expect
inflect.irregular 'media', 'medias'
end
Now execute the scaffold again:
rails g scaffold Media
Now you must have everything in the way you expected. Because you have overwritten the Pluralizations and Singularizations (Inflections) in Ruby on Rails.
I hope it could be useful.
Have you tried to replace your form_for tag with the following?
<%= form_for #profile, :as => :post do |f| %>
It looks like it's trying to treat it as a GET request to "/profile". And, since it is not finding the index action, it craps out. I think forcing it to do a POST will fix this issue.
I'm trying to learn about using remote forms and using the example of an app where the Post model has_many Comments.
Assuming i have used the generic rails scaffolding to create/setup the post/comment model, controllers, default views, routes, etc, -- what should my app/views/posts/show.html look like?
Specific i am confused about:
Where should the comment form post to?
What parameters should be included?
Do i NEED to use a hidden attribute such as f.hidden_field :post_id, :value => #post.id
Thank you!
Assuming that your post has_many comments...
routes.rb (Nested Resources)
resources :posts do
resources :comments
end
In your comments_controller
def create
#post = Post.find(params[:post_id]
#comment = #post.comment.build(params[:comment])
if #comment.save
...
end
In the form:
=form_for [#post, #comment], :remote => true do |f|
=f.text_field :text
=f.submit