Rails has_many nested form No route matches [POST] - ruby-on-rails

I'm getting the above error when I submit my nested form and I'm not sure how to fix it. Here is some info. The exact error is No route matches [POST] "/vehicles/2/vehicle_records/new". The strange thing is that route is present when I run rake routes I see the following entry
/vehicles/:vehicle_id/vehicle_records/new(.:format)
vehicle.rb
class Vehicle < ActiveRecord::Base
has_many :vehicle_records, dependent: :destroy
accepts_nested_attributes_for :vehicle_records
end
vehicle_record.rb
class VehicleRecord < ActiveRecord::Base
belongs_to :vehicle
end
I want the ability to add a vehicle maintenance record linked from the vehicle show view.
views/vehicle/show.html.erb
<h3><b>Vehicle ID:</b> <%= #vehicle.id %></h3>
<p><b>Year:</b> <%= #vehicle.year %></p>
<p><b>Make:</b> <%= #vehicle.make %></p>
<p><b>Model:</b> <%= #vehicle.model %></p>
<p><b>Work Last Performed:</b> <%= #vehicle.make %></p>
<h2>Maintenance Record</h2>
<table>
<tr>
<th>Date Performed</th>
<th>Mileage</th>
<th>Hours</th>
<th>Work Performed</th>
</tr>
<% #vehicle.vehicle_records.each do |vr|%>
<tr>
<td><%= vr.date_performed %></td>
<td><%= vr.mileage %></td>
<td><%= vr.hours %></td>
<td><%= vr.work_performed %></td>
</tr>
<% end %>
</table>
<%= link_to "Add Maintenance Record", new_vehicle_vehicle_record_path(#vehicle) %>
I then link to the vehicle_record new form
views/vehicle_records/new.html.erb
<%= form_for :vehicle_record do |vr| %>
<p>
<%= vr.label :date_performed%><br />
<%= vr.text_field :date_performed%>
</p>
<p>
<%= vr.label :mileage%><br />
<%= vr.text_field :mileage%>
</p>
<p>
<%= vr.label :mileage%><br />
<%= vr.text_field :mileage%>
</p>
<p>
<%= vr.label :hours%><br />
<%= vr.text_field :hours%>
</p>
<p>
<%= vr.label :work_performed%><br />
<%= vr.text_area :work_performed%>
</p>
<p><%= vr.submit "Create Record" %></p>
My vehicle records controller is as follows
vehicle_records_controller.rb
class VehicleRecordsController < ApplicationController
def new
#vehicle_record = VehicleRecord.new
end
def create
#vehicle_record = VehicleRecord.new(params[:vehicle_record])
if #vehicle_record.save
redirect_to vehicles_path
else
flash[:notice] = "Not Saved"
end
end
end
routes.rb
resources :vehicles do
resources :vehicle_records
end

Update: w/o nested forms
So I hear you only want to only edit the vehicle_report without touching the vehicle. Since you already have your vehicle_report as nested resource, you can modify your controller as follows:
class VehicleRecordController < ApplicationController
...
def new
#vehicle = Vehicle.find params[:vehicle_id]
#vehicle_record = #vehicle.vehicle_records.build
end
...
end
and the content of create method is fairly easy. Then you have to change your form to:
<%= form_for #vehicle_record do |v| %>
...
<%= # vehicle record attributes %>
...
<%= v.submit "Submit" %>
And you are good to go!
Original answer: w/ nested forms
First of all, I don't see you using any nested forms. Second, I don't see how your vehicle_record form gets to know to which vehicle it actually belongs (see your controller). In my opinion the simplest way would be to allow adding and removing of vehicle_records directly in vehicles/:id/edit or vehicles/new using nested forms:
<%= form_for #vehicle do |v| %>
...
<%= # vehicle attributes here %>
...
<%= v.fields_for :vehicle_records do |vr| %>
...
<%= # vehicle record attributes %>
...
<%= v.submit "Submit" %>
For existing vehicles the fields_for will render list of all existing vehicle_records and for new ones your can have the following code in your controller to make fields_for render for example three empty vehicle_records:
class VehicleController < ApplicationController
...
def new
#vehicle = new Vehicle
3.times { #vehicle.vehicle_records.build }
end
...
end
If you want to always have a number of free vehicle_records every time you edit an existing vehicle you can use a similar approach for your edit and reuse the aforementioned form:
class VehicleController < ApplicationController
...
def edit
#vehicle = Vehicle.find params[:id]
# Add one blank vehicle record to existing ones
# This is also rendered by fields_for
#vehicle.vehicle_records.build
end
...
end

Related

Ruby on Rails: Why empty value is displayed when form for creation of new objects in DB is included?

I am newbie. I am trying to develop a simple web application involving shops and candies where a shop can have many candies.
I have the following code in my shops/show.html.erb which displays list of candies twice.
<% i=0 %>
<% for candy in #shop.candies do %>
<% i+=1 %>
<%= i %> <%= candy.name %>
<% end %>
<%= form_for([#shop, #shop.candies.build]) do |f| %>
<%= render(:partial => 'form',
:locals => {:f => f, :header => "Add a Candy",
:placeholder => "Enter a candy name"}) %>
<% end %>
<% i=0 %>
<% for candy in #shop.candies do %>
<% i+=1 %>
<%= i %> <%= candy.name %>
<% end %>
My code in _form.html.erb for creating a new candy:
<%= f.text_field(:name, :placeholder=> placeholder, :class=>"form-control custom-input")%>
<%= button_tag( :class => "btn btn-primary mb-2 btn-custom btn-custom-sc") do %>
<i class="fas fa-plus icon"></i>
<% end %>
Code of Shops Controller:
class ShopsController < ApplicationController
def show
#shop = Shop.find(params[:id])
#unshelved_candies = #shop.candies.unshelved_candies
end
private
def shop_params
params.require(:shop).permit(:name)
end
end
Code of Candies Controller:
class CandiesController < ApplicationController
def create
#shop = Shop.find(params[:shop_id])
#candy = #shop.candies.create(candy_params)
redirect_to(shop_path(#shop))
end
private
def candy_params
params.require(:candy).permit(:name)
end
end
end
When I run the code and view it on browser, I notice that it creates an empty candy in the second loop (not in database). However, when I remove the form for creating candies, it behaves as it should. I am unable to understand why it's looping one more time and displaying blank value.
The output of the first loop is the correct one:
Candy 1
Candy 2
Candy 3
And the output of second loop is:
Candy 1
Candy 2
Candy 3
[<---Empty. I am not inserting anything new to the database]
Can anybody tell me why it is displaying a blank value in second loop and how to prevent this extra iteration?
I believe the "extra" candy is the one you're instantiating here:
<%= form_for([#shop, #shop.candies.build]) do |f| %>
The candy name for the new candy is nil, so you're getting the blank.
BTW, this:
<% i=0 %>
<% for candy in #shop.candies do %>
<% i+=1 %>
<%= i %> <%= candy.name %>
<% end %>
Strikes me as non-idiomatic ruby. I would expect to see something more like:
<% #shop.candies.each.with_index(1) do |candy, index| %>
<%= index %> <%= candy.name %>
<% end %>
I guess a brute force way of making sure you don't get that extra candy would be to do something like:
<% #shop.candies.each.with_index(1) do |candy, index| %>
<% unless candy.new_record? %>
<%= index %> <%= candy.name %>
<% end %>
<% end %>
You might also try:
<%= form_for([#shop, #candy]) do |f| %>
Which I believe can be written:
<%= form_for(#shop, #candy) do |f| %>
If you want to save yourself a couple of key strokes (they add up over time).
And then in your ShopsController do:
class ShopsController < ApplicationController
def show
#shop = Shop.find(params[:id])
#candy = Candy.new
#unshelved_candies = #shop.candies.unshelved_candies
end
private
def shop_params
params.require(:shop).permit(:name)
end
end
This is also nice because it avoids:
#shop.candies.build
Which requires that your view knows a lot about the relationship between shop and candies and also requires that your view interacts directly with the database.
Since you're apparently using nested routes, you might want to look at the shallow: true directive.
Also (this is not related to your question), you might want to be thoughtful about the Law of Demeter. I notice you do:
#unshelved_candies = #shop.candies.unshelved_candies
Personally, I would do something more like:
#unshelved_candies = #shop.unshelved_candies
And in Shop, you might have something like:
class Shop < ApplicationRecord
def unselved_candies
candies.unshelved
end
end
And in Candy, something like:
class Candy < ApplicationRecord
class < self
def unshelved
where(shelved: false) # or however you determine a candy is unshelved
end
end
end
Many people would make unshelved a scope, which is another way of doing the same thing.
This way, your ShopsController knows less about the mechanics of the relationships between shops and candies and shelved status. FWIW.

Search on Welcome Controller

I have a Product (imovel) controller where the users can create his own Products (imovels). I am using devise for the authentication and the CRUD (create, Update, Delete) needs login. So for the clients be able to see the products and doesn't need a user, I created the Welcome controller where so it is the root.
In the Index of the Products I use Ransack to do the research in the table and works just fine. (very happy about it).
On the welcome controller I try to do the same thing, but when I submit the search the page gets redirect to the imovels#index.
Controller:
class WelcomeController < ApplicationController
def index
#q = Imovel.ransack(params[:q])
#imovel = #q.result(distinct: true)
end
end
View:
<div class="container text-center">
<%= search_form_for #q do |f| %>
# Search if the name field contains...
<%= f.label :descricao_cont %>
<%= f.search_field :descricao_cont %>
<%= f.submit "Pesquisar", class: "btn btn-primary" %>
<% end %>
</div>
Another thing that can be important in the index (imovels#index) there is a for in a tr and the information is filtered there:
Imovels Index
<tbody>
<% #imovels.each do |imovel| %>
<tr>
<td><%= imovel.id %></td>
<td><%= imovel.descricao %></td>
<% end %>
And in the welcome controller where I need the search, I used Divs:
Welcome Index
<% #imovels.each do |imovel| %>
<div class="card">
<div class="containerImovel">
<h4><b><%= imovel.descricao %></b></h4>
<p><%= imovel.cidade %> - <%= imovel.bairro.nome %> </p>
</div>
</div>
<% end %>
How can I do the search on the divs of welcome controller? Ransack is the better option for it? It is possible to search in the has_many :through association?
Add a path for it in your routes.rb
resources :imovel do
collection do
match 'search' => 'welcome#search', via: [:get, :post], as: :search
end
end
and then add a new controller action to WelcomeController
def search
index
render :index
end
and afterwards modify the search form like so
<%= search_form_for #q, url: search_imovel_path, html: { method: :post } do |f| %>
Be sure to recheck the naming/variables as I'm not completely familiar with your app

Using associations in Ruby on Rails

I'm learning RoR and I'm trying to understand associations. I've got two models - Company [name] and Note [company_id, notes]. As shown, the Note model has a company_id field to reference the primary key in the Company model.
Within a Notes view, I'm trying to display the Company name but I can't seem to get this to work.
I want to display
(SELECT name FROM Company WHERE Company.id=Note.company_id)
instead of note.company_id in the code below.
company.rb
class Company < ActiveRecord::Base
has_many :notes
end
note.rb:
class Note < ActiveRecord::Base
belongs_to :company
default_scope -> { order(date: :desc) }
end
notes/index.html.erb
....
<% #notes.each do |note| %>
<% if note.active %>
<p>
<%= note.date %>
</br>
<%= note.company_id %> - <%= note.contact %>
</br>
<%= note.notes %>
<!-- <td><%= note.active %></td> -->
</p>
<% end %>
<% end %>
....
To answer your specific question, try:
note.company.name
I would also recommend reading up on Rails partials, particularly how to render a collection: http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials

Rails 4 retrieve nested attributes for display in an index.html.erb

Just looking to find out how to retrieve nested images for display on my front page. I have no problems with a standard model but have been unable to find how to bring has_many nested attribute through. All my nested Forms work fine just have neglected the front end.
eg. product has nested product_images. This doesn't look like a clever way of doing it as the last five images uploaded wont necessarily be related to the last five products added.
Could someone please share an example.
cheers
app/controller/home_controller.rb
class HomeController < ApplicationController
def index
#products = Product.last(5)
#product_images = ProductImage.last(5)
end
end
app/views/home/index.html.erb
<% #products.each do |pd| %>
<div><%= pd.product_name %>
<% end %>
<% #product_images.each do |pd| %>
<%= image_tag (pd.product_image(:medium)) %>
<% end %>
</div>
You can try this:
app/controller/home_controller.rb
class HomeController < ApplicationController
def index
#products = Product.last(5)
#product_ids = #products.collect(:id)
#product_images = ProductImage.where(:id => #product_ids).last(5)
end
end
app/views/home/index.html.erb
<% #products.each do |pd| %>
<div><%= pd.product_name %>
<% end %>
<% #product_images.each do |pd| %>
<%= image_tag (pd.product_image(:medium)) %>
<% end %>

How to get model objects in the form with rails check_box?

How do i get checkbox values in the form from the database? I want the form to bring the existing sub category name,and when i check the checkbox to select that particular category name and not create a new one.I have tried ryan bate's railscast but was no help to me. The realationship here is Category has_many SubCategories and SubCategory belongs_to Category.Thank you.
<%= form_for #category ,:url=>{:action =>"create"} do |f| %>
<%=f.text_field :category_name %>
<%= f.fields_for :sub_categories do |s| %>
<% #category.sub_categories.each do |sub|%>
<%=s.check_box "name",{},sub.id %> <!--need help here-->
<%end%>
<%end%>
<%=f.submit "submit"%>
<%end%>
Based on the exchange in the comments, it appears that you want to use the checkboxes to assign SubCategory objects to a Category object. If that's the case, you're association should be that a Category has_and_belongs_to_many :sub_categories. Then your form would look something like:
<%= form_for #category ,:url=>{:action =>"create"} do |f| %>
 <%=f.text_field :category_name %>
<% SubCategories.each do |sc| %>
<div>
<%= check_box_tag :sub_category_ids, sub_category_id, #category.sub_categories.include?(sc), :name => 'category[sub_category_ids][]' -%>
<%= label_tag :sub_category_ids, sc.name -%>
</div>
<% end -%>
<% end %>
Which will show a category form and then list all of the sub_categories that can be assigned or unassigned by checking the checkboxes.
You will also need a join table "categories_sub_categories" for this new association and logic (likely in your controller) to handle the actual assignment.
example for your category_controller.rb
def create
#category = Category.find(params[:id])
#use the checked sub_category_ids from the form to find and assign the sub_categories.
assigned_sub_categories = SubCategory.find(params[:category][:sub_category_ids]) rescue []
#category.sub_categories = assigned_sub_categories
if #category.save
…
else
…
end
end

Resources