Rails nested routing + shallow edit not working - ruby-on-rails

I would like to use some routing like this:
resources :customers do
resources :electricity_counters, :shallow => true do
resources :electricity_bills, :shallow => true
end
end
Creating a electricity_counter works fine, but edit doesn't work as expected..
If I visit electricity_counters/1/edit I only get blank fields and all my data is missing.
My _form.html.erb for that starts like this
<%= form_for([#customer, #customer.electricity_counters.build]) do |f| %>
and the controller methods for new and edit are like this:
# GET customers/1/electricity_counters/new
def new
#customer = Customer.find(params[:customer_id])
#electricity_counter = #customer.electricity_counters.build
end
# GET /electricity_counters/1/edit
def edit
#electricity_counter = ElectricityCounter.find(params[:id])
#customer = #electricity_counter.customer
end
In debug it seems to be that my #customer variable isn't set right.. but maybe I'm just to stupid to use that aptana debugger;)
The electricity_counter model has the association to a customer set by:
belongs_to :customer
So what am I doing wrong?

Your problem is this line.
<%= form_for([#customer, #customer.electricity_counters.build]) do |f| %>
It builds a new electricity_counter no matter what you want to do. Since you're handling that in the controller.
But since you want to use the same _form partial for both new and edit you have to be able to change the form path. Basically I ended up doing something like this:
Controller
def new
#customer = Customer.find(params[:customer_id])
#electricity_counter = #customer.electricity_counters.build
#path = [#customer, #electricity_counter]
end
def edit
#electricity_counter = ElectricityCounter.find(params[:id])
#customer = #electricity_counter.customer
#path = #electricity_counter
end
Form
<%= form_for(#path) do |f| %>
Also your routes.rb is off change it to this
resources :customers, :shallow => true do
resources :electricity_counters, :shallow => true do
resources :electricity_bills
end
end

Related

Following Rails tutorial, undefined method 'create' for nil:NilClass, with nested resource

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.

How can I determine which redirect to make in Rails?

I have two paths that my application can take.
Path 1: Is editing a valid record.
User goes to persons#show
User clicks edit to go to persons#edit
After update leads back to persons#show
Path 2: Is editing an invalid record.
User goes to persons#invalid_records
User clicks edit to go to persons#edit
After update (if it succeeds) leads back to persons#show
How can I have Path 2 end up back at persons#invalid_records instead of persons#show?
edit posting routes as requested:
resources :persons do
scope module: :persons do
resources :notes
resources :reports
end
collection do
match 'invalid_records' => 'persons#invalid_records', via [:get], as :invalid_records
end
member do
get 'transactions'
end
end
So I wanted to post an answer to help get anyone started that might be confused. Thanks to #MaxWilliams for the idea.
I added the following...
<%= link_to "Edit", edit_person_path(record, :invalid_record => true) %>
along with this hidden field on my _form.html.erb
<%= f.hidden_field :invalid_record, :value => params[:invalid_record] %>
Finally on the persons_controller.rb.
def update
respond_to do |format|
if #person.update(person_params)
if params[:person][:invalid_record] == true
#...
else
#...
end
else
# ...
end
end
end

undefined method ..._index_path Ruby on Rails

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.

Nested resource form paths

this is my form
<%= form_for([#game,#message_template]) do |f| %>
and in my controller I do this:
def edit
#message_template = MessageTemplate.find(params[:id])
#game = Game.where(:slug => params[:game_id])
end
My route is like this /games/:game_id/message_templates/:id
But I am getting this
undefined method `model_name' for #
Try setting that arel search to return the first object (for #game).
def edit
#message_template = MessageTemplate.find(params[:id])
#game = Game.where(:slug => params[:game_id]).first
end
routes.rb:
#based on your comment, this will give you all (and likely more) of the paths you will need.
resources :games do
resources :message_templates
end

form for nested resource

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

Resources