Unable to route to child resource - Nested routes - ruby-on-rails

I've been banging my head against this one for few days now ......
my routes.rb file looks like this:
resources :vehicles do
member do
resources :services
end
end
my service.rb file shows:
class Service < ActiveRecord::Base
belongs_to :vehicle
end
my vehicle.rb file shows:
class Vehicle < ActiveRecord::Base
has_many :services, :dependent => :destroy
end
Here is services_controller.rb
class ServicesController < ApplicationController
def index
#vehicle = Vehicle.find(params[:id])
#services = #vehicle.services.order('created_at DESC')
end
def show
#vehicle = Vehicle.find(params[:id])
#service = Vehicle.services.find(params[:service_id])
end
def new
#vehicle = Vehicle.find(params[:id])
#service = #vehicle.services.new
end
def create
#vehicle = Vehicle.find(params[:id])
#service = #vehicle.services.build(service_params)
if #service.save
redirect_to #vehicle
else
render :new
end
end
private
def service_params
params.require(:service).permit(:service_option, :odometer,
:current_service, :price, :comments, :next_service)
end
end
And here is the services#index
<h1>Services</h1>
<% #services.each do |s| %>
<%= link_to "Details", service_path(s) %>
<%= s.created_at %>
<%= s.service_option %>
<%= s.odometer %><br>
<% end %>
Clicking on the "Details" link or manually entering the URL
http://localhost:3000/vehicles/2/services/7
gives my systematically the same routing error:
ActiveRecord::RecordNotFound in ServicesController#show
Couldn't find Vehicle with 'id'=7
Extracted source (around line #10):
def show
#vehicle = Vehicle.find(params[:id])
#service = Vehicle.services.find(params[:service_id])
end
It passes a SERVICE ID to a vehicle objet / variable and I don't know why!
Thanks a million!

In your services#show method you are fetching the vehicle and service with wrong params. Also Vehicle.services is wrong, it should be #vehicle.services
def show
#vehicle = Vehicle.find(params[:vehicle_id])
#service = #vehicle.services.find(params[:id])
end
Update:
You should remove member in the routes, else it will produce the routes like
service GET /vehicles/:id/services/:id(.:format) services#show
which are irrelevant
resources :vehicles do
resources :services
end
So now you will have routes like
service GET /vehicles/:vehicle_id/services/:id(.:format) services#show
which are relevant and you can fetch the vehicle with params[:vehicle_id] and service with params[:id]
Don't forget to change the route helpers. For instance service_path will become vehicle_service_path
<%= link_to "Details", vehicle_service_path(#vehicle,s) %>

Related

How should I update a resource that is fully depend on other resource

Let's say I have those two models:
class Post < ApplicationRecord
belongs_to :site
end
class Site < ApplicationRecord
has_many :posts
end
In order to create a post, I need to know the site id. Right now I have a route that points to PostsController#create:
post 'posts', to: 'posts#update'
Should I expect the user to send the site_id in the body of the request?
# config/routes.rb
resources :sites do
resources :posts
end
This creates nested routes. Run $ rails routes to see the routes created.
Should I expect the user to send the site_id in the body of the request?
No. A nested route describes the relationship between the two resources. Its very obvious by looking at the path that POST /sites/1/posts will create a post belonging to a site.
It would be ok to pass a site id in the params if you are using shallow nesting and the user can change which site a post belongs to when updating.
# app/controllers/posts_controller.rb
class PostsController
before_action :set_site
before_action :set_post, only: [:show, :edit, :update]
# GET /sites/1/posts/1
def show
end
# GET /sites/1/posts
def index
#posts = #site.posts
end
# GET /sites/1/posts/new
def new
#post = #site.posts.new
end
# POST /sites/1/posts
def create
#post = #site.posts.new(post_params)
if #post.save
redirect_to #post
else
render :new
end
end
# PATCH|PUT /sites/1/posts
def update
if #post.update(post_params)
redirect_to #post
else
render :edit
end
end
# GET /sites/1/posts/edit
def edit
end
private
def set_site
#site = Site.includes(:posts).find(params[:site_id])
end
def set_post
#post = #site.posts.find(params[:id])
end
def post_params
params.require(:post).permit(:title) # ...
end
end
# app/views/posts/_form.html.erb
<%= form_for [#site, #post] do |f| %>
# ...
<% end %>
# app/views/posts/new.html.erb
<%= render partial: 'form' %>
# app/views/posts/edit.html.erb
<%= render partial: 'form' %>

Rails how to hide form after save?

this is the case:
models/product.rb
belongs_to :brand
models/brand.rb
has_many :products
controllers/products_controller.rb
class ProductsController < ApplicationController
def new
#product = Product.new
#brands = Brand.all
end
def create
#product = Product.new(params[:product])
if #product.save
redirect_to :show
else
render :new, format: :html
end
end
end
On product create the user can add a brand name and if the user add a brand name on next time to create a product the form for the brand did not show again.
Someone please have a idea how to do something like that on rails?
That's something to add in your view.
Use a condition around your brand form :
form #product do |f|
f.text_field :name
if not #product.new_record?
f.select_field :brand_id, #brands, :id
end
end

Find by linked fields Rails 5

I have a model that works like this:
class User < ApplicationRecord
has_many :deals
...
class Deal < ApplicationRecord
belongs_to :user
has_many :clients
...
class Clients < ApplicationRecord
belongs_to :deal
If I want to find all the clients listed on a certain deal I can enter Client.find_by(deal_id: x) in the console where x is the deal ID I want, but when I try to list them all on a page I'm doing something wrong.
Here's the relevant resource route
Rails.application.routes.draw do
resources :deals, only: [:show]
end
I think the problem is I'm not doing the find correctly in the controller
class DealsController < ApplicationController
def show
#deal = Deal.find_by_id(params[:id])
#client = Client.find_by(deal_id: params[:id])
end
end
On the page I'm trying to list clients like this:
<div class="client">
<% #client.each do |c| %>
<%= c.name %>
<% end %>
</div>
But the error is undefined method each' for #<Client:0x007f8e4cdecfb0>
I thought it would be pretty simple because the :id that's returned from /deal/:id is shared by both, but I'm stumped.
find_by will return only one object, each is a method defined for arrays.
So if you want clients for a particular deal, you can do
#deal = Deal.find_by_id(params[:id])
#clients = #deal.clients
or
#deal = Deal.find_by_id(params[:id])
#clients = Client.where(deal_id: params[:id])
and in view
<% #clients.each do |c| %>
<%= c.name %>
<% end %>

Rails4 Entering data from one controller to multiple models

I have someone of a unique problem. I have 3 tables in the database that I need to populate with data. All tables are in relation to each other. The first table's info will be static and populated from a hash. The second table is the table that is usually targeted with data.
I am having a tough time trying to add data into the second table using strong parameters. I get an error param is missing or the value is empty: entries
Modles:
client.rb
class Client < ActiveRecord::Base
has_many :entries
end
Entry.rb
class Entry < ActiveRecord::Base
belongs_to :client_name
has_many :extra_data
end
extra_data.rb
class ExtraData < ActiveRecord::Base
belongs_to :entries
end
class ClientsController < ApplicationController
before_action :set_client, only: [:show, :update, :destroy, :edit]
# submit for all intended purposes.
#
def new
#entries = Entry.new()
end
def create
#client = Client.new(CLEINT_ATTR)
if #client.save
#entries = Entry.new(submit_params)
redirect_to action: :index
else
flash.alert "you failed at life for today."
redirect_to action: :index
end
end
.
.
.
private
def submit_params
params.require(:entries).permit( :full_name,:email,:opt_in )
end
def set_client
#client = Client.find(params[:id])
end
end
form
<%= simple_form_for(:client, url: {:controller => 'clients', :action => 'create'}) do |f| %>
<%= f.input :full_name %>
<%= f.input :email %>
<%= f.input :opt_in %>
<%= f.button :submit, class: "btn-primary" %>
<% end %>
Routes:
Rails.application.routes.draw do
resources :clients do
resources :entries do
resources :extra_data
end
end
root 'clients#index'
end
In the Database Client data goes in with out a problem. I am having a problem getting the data from the form itself.
This answer is the culmination of a few different parts.
I figured out I was not saving any data into the model. So I needed to make another if statement.
def create
#client = Client.new(CLEINT_ATTR)
if #client.save
#entries = Entry.new(submit_params)
if #entries.save
flash[:alert] = "Failure! everything is working."
redirect_to action: :index
else
flash[:alert] = "Success! at failing."
end
else
flash[:alert] = "you failed at life for today."
redirect_to action: :thanks
end
end
Also changing the form from :entries Helped. I also had a typo in my permit statment. I had :opt_in when I needed to use :optin Thanks #tmc

Ruby on Rails Saving in two tables from one form

I have two models Hotel and Address.
Relationships are:
class Hotel
belongs_to :user
has_one :address
accepts_nested_attributes_for :address
and
class Address
belongs_to :hotel
And I need to save in hotels table and in addresses table from one form.
The input form is simple:
<%= form_for(#hotel) do |f| %>
<%= f.text_field :title %>
......other hotel fields......
<%= f.fields_for :address do |o| %>
<%= o.text_field :country %>
......other address fields......
<% end %>
<% end %>
Hotels controller:
class HotelsController < ApplicationController
def new
#hotel = Hotel.new
end
def create
#hotel = current_user.hotels.build(hotel_params)
address = #hotel.address.build
if #hotel.save
flash[:success] = "Hotel created!"
redirect_to #hotel
else
render 'new'
end
end
But this code doesn't work.
ADD 1
Hotel_params:
private
def hotel_params
params.require(:hotel).permit(:title, :stars, :room, :price)
end
ADD 2
The main problem is I don't know how to render form properly. This ^^^ form doesn't even include adress fields (country, city etc.). But if in the line
<%= f.fields_for :address do |o| %>
I change :address to :hotel, I get address fields in the form, but of course nothing saves in :address table in this case. I don't understand the principle of saving in 2 tables from 1 form, I'm VERY sorry, I'm new to Rails...
You are using wrong method for appending your child with the parent.And also it is has_one relation,so you should use build_model not model.build.Your new and create methods should be like this
class HotelsController < ApplicationController
def new
#hotel = Hotel.new
#hotel.build_address #here
end
def create
#hotel = current_user.hotels.build(hotel_params)
if #hotel.save
flash[:success] = "Hotel created!"
redirect_to #hotel
else
render 'new'
end
end
Update
Your hotel_params method should look like this
def hotel_params
params.require(:hotel).permit(:title, :stars, :room, :price,address_attributes: [:country,:state,:city,:street])
end
You should not build address again
class HotelsController < ApplicationController
def new
#hotel = Hotel.new
end
def create
#hotel = current_user.hotels.build(hotel_params)
# address = #hotel.address.build
# the previous line should not be used
if #hotel.save
flash[:success] = "Hotel created!"
redirect_to #hotel
else
render 'new'
end
end
Bottom line here is you need to use the f.fields_for method correctly.
--
Controller
There are several things you need to do to get the method to work. Firstly, you need to build the associated object, then you need to be able to pass the data in the right way to your model:
#app/models/hotel.rb
Class Hotel < ActiveRecord::Base
has_one :address
accepts_nested_attributes_for :address
end
#app/controllers/hotels_controller.rb
Class HotelsController < ApplicationController
def new
#hotel = Hotel.new
#hotel.build_address #-> build_singular for singular assoc. plural.build for plural
end
def create
#hotel = Hotel.new(hotel_params)
#hotel.save
end
private
def hotel_params
params.require(:hotel).permit(:title, :stars, :room, :price, address_attributes: [:each, :address, :attribute])
end
end
This should work for you.
--
Form
Some tips for your form - if you're loading the form & not seeing the f.fields_for block showing, it basically means you've not set your ActiveRecord Model correctly (in the new action)
What I've written above (which is very similar to that written by Pavan) should get it working for you

Resources