I was trying to work with simple nested models & forms but have failed eventually. I don't understand what wrong am I doing? Trying to implement simple nested model. A parent having many child. Can anyone please help me out. Thanks.
Here are the models:
parent.rb
class Parent < ActiveRecord::Base
has_many :childs
accepts_nested_attributes_for :childs
end
child.rb
class Child < ActiveRecord::Base
belongs_to :parent
end
parents_controller.rb
class ParentsController < ApplicationController
before_action :set_parent, only: [:show, :edit, :update, :destroy]
# GET /parents
# GET /parents.json
def index
#parents = Parent.all
end
# GET /parents/1
# GET /parents/1.json
def show
end
# GET /parents/new
def new
#parent = Parent.new
#parent.childs.new
end
# GET /parents/1/edit
def edit
end
# POST /parents
# POST /parents.json
def create
#parent = Parent.create(parent_params)
respond_to do |format|
if #parent.save
format.html { redirect_to #parent, notice: 'Parent was successfully created.' }
format.json { render :show, status: :created, location: #parent }
else
format.html { render :new }
format.json { render json: #parent.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /parents/1
# PATCH/PUT /parents/1.json
def update
respond_to do |format|
if #parent.update(parent_params)
format.html { redirect_to #parent, notice: 'Parent was successfully updated.' }
format.json { render :show, status: :ok, location: #parent }
else
format.html { render :edit }
format.json { render json: #parent.errors, status: :unprocessable_entity }
end
end
end
# DELETE /parents/1
# DELETE /parents/1.json
def destroy
#parent.destroy
respond_to do |format|
format.html { redirect_to parents_url, notice: 'Parent was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_parent
#parent = Parent.find(params[:id])
end
def parent_params
params.require(:parent).permit(:name, childs_attributes: [:name])
end
end
parent form.html.erb
<%= form_for(#parent) do |f| %>
<% if #parent.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#parent.errors.count, "error") %> prohibited this parent from being saved:</h2>
<ul>
<% #parent.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label 'Parent name' %><br>
<%= f.text_field :name %>
</div>
<%= f.fields_for :child do |c| %>
<div class="field">
<%= c.label 'Child Name' %><br>
<%= c.text_field :name %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
The parent is getting created but the child is not. The child table has parent_id for associating both models.
Thanks in advance
I believe the plural of child is not childs, but children, and Rails knows this. You need to change your has_many association accordingly.
EDIT: As #pavan pointed out, change all the occurences in your code, not only the association.
Related
I’m working on a project that will utilize nested forms that incorporates Rails 6.1.3 and Bootstrap 5.1.2.
I’m having difficulty getting the nested form feature to work.
Project GitHub: cjmccormick88/testapp-nested
There are two models: client and shipping address.
Client accepts nested attributes for the shipping address model. A client can have many shipping addresses.
Authentication is being handled by Devise. Bootstrap is used for styling. Audited is used for audit trail.
Clients Controller
class ClientsController < ApplicationController
before_action :set_client, only: %i[ show edit update destroy ]
# GET /clients or /clients.json
def index
#clients = Client.all
end
# GET /clients/1 or /clients/1.json
def show
end
# GET /clients/new
def new
#client = Client.new
#client.shipping_addresses.build
end
# GET /clients/1/edit
def edit
end
# POST /clients or /clients.json
def create
#client = Client.new(client_params)
#client.shipping_addresses.build(client_params[:shipping_addresses_attributes])
#client.save
respond_to do |format|
if #client.save
format.html { redirect_to #client, notice: "Client was successfully created." }
format.json { render :show, status: :created, location: #client }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #client.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /clients/1 or /clients/1.json
def update
respond_to do |format|
if #client.update(client_params)
format.html { redirect_to #client, notice: "Client was successfully updated." }
format.json { render :show, status: :ok, location: #client }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #client.errors, status: :unprocessable_entity }
end
end
end
# DELETE /clients/1 or /clients/1.json
def destroy
#client.destroy
respond_to do |format|
format.html { redirect_to clients_url, notice: "Client was successfully destroyed." }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_client
#client = Client.find(params[:id])
end
# Only allow a list of trusted parameters through.
def client_params
params.require(:client).permit(:client_name, shipping_addresses_attributes: [:id, :address_line1, :address_line2, :city, :state, :country])
end
end
Client Model
class Client < ApplicationRecord
audited
has_many :shipping_addresses, :inverse_of => :client, autosave: true
accepts_nested_attributes_for :shipping_addresses
end
Shipping Address Model
class ShippingAddress < ApplicationRecord
audited
belongs_to :client
validates :client, :presence => true
end
Client View Form Partial
<%= form_with(model: client) do |form| %>
<% if client.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(client.errors.count, "error") %> prohibited this client from being saved:</h2>
<ul>
<% client.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :client_name %>
<%= form.text_field :client_name %>
</div>
<%= form.fields_for #client.shipping_addresses.build do |s| %>
<div class="field">
<%= s.label :address_line1, 'Address Line 1' %>
<%= s.text_field :address_line1 %>
</div>
<div class="field">
<%= s.label :address_line2, 'Address Line 2' %>
<%= s.text_field :address_line2 %>
</div>
<div class="field">
<%= s.label :city, 'City' %>
<%= s.text_field :city %>
</div>
<div class="field">
<%= s.label :state, 'State' %>
<%= s.text_field :state %>
</div>
<div class="field">
<%= s.label :country, 'Country' %>
<%= s.text_field :country %>
</div>
<% end %>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
In addition, there is a controller for shipping addresses if someone chooses to view those pages on their own.
Shipping Addresses Controller
class ShippingAddressesController < ApplicationController
before_action :set_shipping_address, only: %i[ show edit update destroy ]
# GET /shipping_addresses or /shipping_addresses.json
def index
#shipping_addresses = ShippingAddress.all
end
# GET /shipping_addresses/1 or /shipping_addresses/1.json
def show
end
# GET /shipping_addresses/new
def new
#shipping_address = ShippingAddress.new
end
# GET /shipping_addresses/1/edit
def edit
end
# POST /shipping_addresses or /shipping_addresses.json
def create
#shipping_address = ShippingAddress.new(shipping_address_params)
respond_to do |format|
if #shipping_address.save
format.html { redirect_to #shipping_address, notice: "Shipping address was successfully created." }
format.json { render :show, status: :created, location: #shipping_address }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #shipping_address.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /shipping_addresses/1 or /shipping_addresses/1.json
def update
respond_to do |format|
if #shipping_address.update(shipping_address_params)
format.html { redirect_to #shipping_address, notice: "Shipping address was successfully updated." }
format.json { render :show, status: :ok, location: #shipping_address }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #shipping_address.errors, status: :unprocessable_entity }
end
end
end
# DELETE /shipping_addresses/1 or /shipping_addresses/1.json
def destroy
#shipping_address.destroy
respond_to do |format|
format.html { redirect_to shipping_addresses_url, notice: "Shipping address was successfully destroyed." }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_shipping_address
#shipping_address = ShippingAddress.find(params[:id])
end
# Only allow a list of trusted parameters through.
def shipping_address_params
params.require(:shipping_address).permit(:address_line1, :address_line2, :city, :state, :country, :client_id)
end
end
Behavior of the Application
The application accepts the entry of the client form and it manipulates the model for the shipping address; however, the only entry in the table on each row is the client_id value for the client foreign key. It is not committing the other components of the hash into the table.
Screen Display
Things Tried
I've tried the application posted on GitHub at stevepolitodesign/rails-nested-form-app.
I've tried a suggestion made from a similar post on rails forum: difficulties-with-nested-form-implementation-rails-6-1-3/78776/3.
I've gone through as much documentation as I can track down on the standard rails guides for nested forms.
Results from all of these did not yield good results. Item # 1 was a decent app in terms of the pathway it takes; however, when you are using bootstrap it does not seem to work. It could be that the code there has to be modified some to allow that functionality. So far, any posts made for a request regarding bootstrap with that design have not yielded fruit.
Scope
I'm looking to understand the problem that is happening and/or find a better way to accomplish this function that cooperates well with Bootstrap use.
I am currently working with nested resources that have a, has_many through association.
Trying to simply edit my dynamic_elements values, but the when the edit page is generated the form button is primed to complete a post action. I've read a little that rails does this because some browsers don't support PUT so it just goes ahead with a Post. Ive also read to keep the form from making this duplicate that all I have to do is put the :id in the strong params. The only problem with this is that :id is not making it into the paramaters when I submit the form. The :id is present in params for the edit view, but as soon as I hit submit they are no where to be found. Here's some code:
here, you can see the paramters of {"dynamic_page_id"=>"2", "id"=>"31"} are present for the edit method/view but as soon as the update method is called, the "id"=>"31" is lost
enter image description here
routes:
namespace :admin do
resources :dynamic_pages do
resources :dynamic_elements
end
end
rake routes:
enter image description here
Models:
module Admin
class DynamicElement < ApplicationRecord
has_many :dynamic_page_elements
has_many :dynamic_pages, :through => :dynamic_page_elements
end
end
module Admin
class DynamicPage < ApplicationRecord
has_many :dynamic_page_elements
has_many :dynamic_elements, :through => :dynamic_page_elements
end
end
module Admin
class DynamicPageElement < ApplicationRecord
belongs_to :market_page
belongs_to :market_element
end
end
Controller:
module Admin
class DynamicElementsController < ApplicationController
before_action :set_dynamic_element, only: [:show, :edit, :update, :destroy]
before_action :set_dynamic_page, only: [:index, :show, :edit, :update, :destroy]
def index
#dynamic_elements = DynamicElement.all
# #dynamic_page = DynamicPageElement.find(params[:dynamic_page_id])
end
def show
end
def new
#dynamic_element = DynamicElement.new
end
def edit
#dynamic_element = DynamicElement.find(params[:id])
#styles_hash = #dynamic_element.style
end
def create
# #dynamic_element = DynamicElement.find(params[:id])
#dynamic_element = DynamicElement.new(dynamic_element_params)
respond_to do |format|
if #dynamic_element.save
format.html { redirect_to admin_dynamic_page_dynamic_elements_url, notice: 'Dynamic element was successfully created.' }
format.json { render :show, status: :created, location: #dynamic_element }
else
format.html { render :new }
format.json { render json: #dynamic_element.errors, status: :unprocessable_entity }
end
end
#dynamic_page_element = DynamicPageElement.new(dynamic_element_id: #dynamic_element.id, dynamic_page_id: params[:dynamic_page_id])
#dynamic_page_element.save
end
def update
respond_to do |format|
if #dynamic_element.update(dynamic_element_params)
format.html { redirect_to admin_dynamic_page_dynamic_elements_path, notice: 'Dynamic element was successfully updated.' }
format.json { render :show, status: :ok, location: #dynamic_element }
else
format.html { render :edit }
format.json { render json: #dynamic_element.errors, status: :unprocessable_entity }
end
end
end
def destroy
#dynamic_page_element = DynamicPageElement.where(dynamic_element_id: params[:id])[0]
#dynamic_page_element.destroy
#dynamic_element.destroy
respond_to do |format|
format.html { redirect_to admin_dynamic_page_dynamic_elements_path, notice: 'Dynamic element was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_dynamic_element
#dynamic_element = DynamicElement.find(params[:id])
end
def set_dynamic_page
#dynamic_page = DynamicPage.find(params[:dynamic_page_id])
end
def dynamic_element_params
params.permit(:style, :description, :id)
end
end
end
form:
<%= form_with(url:admin_dynamic_page_dynamic_elements_url) do |form| %>
<% if dynamic_element.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(dynamic_element.errors.count, "error") %>
prohibited this dynamic_element from being saved:</h2>
<ul>
<% dynamic_element.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :description %>
<%= form.text_field :description, value: #dynamic_element.description %>
</div>
<div class="field">
<%= form.label :style %>
<%= form.text_area :style, value: #styles_hash %>
</div>
<h5>Current Styles</h5>
<ul style="list-style-type: none;">
<% #dynamic_element.style.keys.zip(#dynamic_element.style.values).each do |key, value| %>
<li>"<%= key %>" => "<%= value %>", </li>
<% end %>
</ul>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
instead of url:admin_dynamic_page_dynamic_elements_url in my form helper, i've tried, model: #market_page, but only to be thrown this error:
undefined method `admin_dynamic_element_path' for #<#<Class:0x007ffe56cf57b8>:0x007ffe56c45340>
Did you mean? admin_dynamic_page_path
admin_dynamic_pages_path
enter image description here
I just cannot seem to figure this out, any help would be appreciated. If I need to provide more info please tell me! Thank you!
I have the following resources defined:
resources :buildings do
resources :buildings_regular_hours
end
My models are are follows:
class Building < ApplicationRecord
has_many :building_regular_hours
def to_s
name
end
end
class BuildingsRegularHours < ApplicationRecord
belongs_to :building
end
I am attempting to create a form to allow for creating and editing of BuildingRegularHours. Currently the form I have will display on #edit, but will not display on #new.
new.html.erb:
<%= render 'form', buildings_regular_hour: #buildings_regular_hour, building: #building %>
edit.html.erb:
<h1>Editing Buildings Regular Hour</h1>
<%= render 'form', buildings_regular_hour: #buildings_regular_hour, building: #building %>
<%= link_to 'Back', building_buildings_regular_hour_path(#building) %>
_form.html.erb:
<%= form_for([building,buildings_regular_hour]) do |f| %>
<% if buildings_regular_hour.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(buildings_regular_hour.errors.count, "error") %> prohibited this buildings_regular_hour from being saved:</h2>
<ul>
<% buildings_regular_hour.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :building_id %>
<%= f.text_field :building_id %>
</div>
<div class="field">
<%= f.label :start_date %>
<%= f.date_select :start_date %>
</div>
<div class="field">
<%= f.label :end_date %>
<%= f.date_select :end_date %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
buildings_regular_hours_controller.rb:
class BuildingsRegularHoursController < ApplicationController
before_action :set_buildings_regular_hour, only: [:show, :edit, :update, :destroy]
before_action :set_building
# GET /buildings_regular_hours
# GET /buildings_regular_hours.json
def index
#buildings_regular_hours = BuildingsRegularHours.all
end
# GET /buildings_regular_hours/1
# GET /buildings_regular_hours/1.json
def show
end
# GET /buildings_regular_hours/new
def new
#buildings_regular_hour = BuildingsRegularHours.new
end
# GET /buildings_regular_hours/1/edit
def edit
end
# POST /buildings_regular_hours
# POST /buildings_regular_hours.json
def create
#buildings_regular_hour = BuildingsRegularHours.new(buildings_regular_hour_params)
respond_to do |format|
if #buildings_regular_hour.save
format.html { redirect_to #buildings_regular_hour, notice: 'Buildings regular hours was successfully created.' }
format.json { render :show, status: :created, location: #buildings_regular_hour }
else
format.html { render :new }
format.json { render json: #buildings_regular_hour.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /buildings_regular_hours/1
# PATCH/PUT /buildings_regular_hours/1.json
def update
respond_to do |format|
if #buildings_regular_hour.update(buildings_regular_hour_params)
format.html { redirect_to #buildings_regular_hour, notice: 'Buildings regular hours was successfully updated.' }
format.json { render :show, status: :ok, location: #buildings_regular_hour }
else
format.html { render :edit }
format.json { render json: #buildings_regular_hour.errors, status: :unprocessable_entity }
end
end
end
# DELETE /buildings_regular_hours/1
# DELETE /buildings_regular_hours/1.json
def destroy
#buildings_regular_hour.destroy
respond_to do |format|
format.html { redirect_to buildings_regular_hours_index_url, notice: 'Buildings regular hours was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_buildings_regular_hour
#buildings_regular_hour = BuildingsRegularHours.find(params[:id])
end
def set_building
#building = Building.find(params[:building_id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def buildings_regular_hour_params
params.require(:buildings_regular_hour).permit(:building_id, :start_date, :end_date, :sunday_id, :monday_id, :tuesday_id, :wednesday_id, :thursday_id, :friday_id, :saturday_id)
end
end
Having added a BuildingRegularHours via console, I attempted the #edit action, and it works just fine, displaying the form as expected. However, when I attempt the #new action, I receive the following error:
Showing /Users/shawn/Documents/uga/library_hours/app/views/buildings_regular_hours/_form.html.erb where line #1 raised:
undefined method `building_buildings_regular_hours_index_path` for #<#<Class:0x007fe9589e2890>:0x007fe95f9bbb30>
Did you mean? building_buildings_regular_hours_path
building_buildings_regular_hour_path
building_buildings_regular_hours_url
building_buildings_regular_hour_url
Extracted source (around line #1):
1 <%= form_for([building,buildings_regular_hour]) do |f| %>
2 <% if buildings_regular_hour.errors.any? %>
3 <div id="error_explanation">
4 <h2><%= pluralize(buildings_regular_hour.errors.count, "error") %> prohibited this buildings_regular_hour from being saved:</h2>
5
6 <ul>
Trace of template inclusion: app/views/buildings_regular_hours/new.html.erb
I note that I have properly nested the resources in the form_for tag, that both #building and #building_regular_hour are set by the controller, and that I am calling the form in exactly the same way for both #edit and #new. This is all I've had to do previously to make nested resources work, so I'm at a bit of a loss as what to do next.
Please note I have not attempted to make the form work yet - I know there is work to be done there. I just am trying to get #new to display the form.
You need to correct the association
class Building < ApplicationRecord
has_many :buildings_regular_hours
def to_s
name
end
end
class BuildingsRegularHour < ApplicationRecord
belongs_to :building
end
Your model name should always be singular BuildingsRegularHour or it will create issues with routes and associations
I'm developing an app in which users can have a personal food diary.
It's pretty simple: a user can search for a nutrient on a Nutrients table (let's say milk) - currently through a pretty simple search form - and then should be able to save the amount he consumed of this nutrient (together with the nutrient_id) on a second table, which is called Diaries (that holds the nutrient_id as foreign key and an integer field called "amount").
My search works. I can also create new records in Diaries but I have to type in the nutrient_id manually.
My question is now how I can make this easily work? Ideally a user finds a nutrient clicks on it and will be redirected to a page that shows this nutrient together with a field for "amount" and a save button to save both information (nutrient_id and amount) on the Diaries table.
At the end of the day I think the user will be directed to the new action of my diary controller - the question is how my app sets the nutrient_id for this action for the nutrient the user selected before?
Sorry if this is a too simple question but I just started coding few weeks ago.
Thanks a lot for help!
My code looks like as follows:
nutrient.rb
class Nutrient < ActiveRecord::Base
has_many :diaries
def self.search(search)
where("name LIKE ?", "%#{search}%")
end
end
diary.rb
class Diary < ActiveRecord::Base
belongs_to :nutrient
end
nutrients_controller.rb
class NutrientsController < ApplicationController
before_action :set_nutrient, only: [:show, :edit, :update, :destroy]
# GET /nutrients
# GET /nutrients.json
def index
#nutrients = Nutrient.all
end
def search
if params[:search]
#nutrients = Nutrient.search(params[:search]).order("created_at DESC")
if #nutrients.present?
#nutrients
else
flash[:notice] = "Nutrient not found in database"
end
end
end
# GET /nutrients/1
# GET /nutrients/1.json
def show
end
# GET /nutrients/new
def new
#nutrient = Nutrient.new
end
# GET /nutrients/1/edit
def edit
end
# POST /nutrients
# POST /nutrients.json
def create
#nutrient = Nutrient.new(nutrient_params)
respond_to do |format|
if #nutrient.save
format.html { redirect_to #nutrient, notice: 'Nutrient was successfully created.' }
format.json { render :show, status: :created, location: #nutrient }
else
format.html { render :new }
format.json { render json: #nutrient.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /nutrients/1
# PATCH/PUT /nutrients/1.json
def update
respond_to do |format|
if #nutrient.update(nutrient_params)
format.html { redirect_to #nutrient, notice: 'Nutrient was successfully updated.' }
format.json { render :show, status: :ok, location: #nutrient }
else
format.html { render :edit }
format.json { render json: #nutrient.errors, status: :unprocessable_entity }
end
end
end
# DELETE /nutrients/1
# DELETE /nutrients/1.json
def destroy
#nutrient.destroy
respond_to do |format|
format.html { redirect_to nutrients_url, notice: 'Nutrient was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_nutrient
#nutrient = Nutrient.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def nutrient_params
params.require(:nutrient).permit(:name)
end
end
diaries_controller.rb
class DiariesController < ApplicationController
before_action :set_diary, only: [:show, :edit, :update, :destroy]
# GET /diaries
# GET /diaries.json
def index
#diaries = Diary.all
end
# GET /diaries/1
# GET /diaries/1.json
def show
end
# GET /diaries/new
def new
#diary = Diary.new
end
# GET /diaries/1/edit
def edit
end
# POST /diaries
# POST /diaries.json
def create
#diary = Diary.new(diary_params)
respond_to do |format|
if #diary.save
format.html { redirect_to #diary, notice: 'Diary was successfully created.' }
format.json { render :show, status: :created, location: #diary }
else
format.html { render :new }
format.json { render json: #diary.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /diaries/1
# PATCH/PUT /diaries/1.json
def update
respond_to do |format|
if #diary.update(diary_params)
format.html { redirect_to #diary, notice: 'Diary was successfully updated.' }
format.json { render :show, status: :ok, location: #diary }
else
format.html { render :edit }
format.json { render json: #diary.errors, status: :unprocessable_entity }
end
end
end
# DELETE /diaries/1
# DELETE /diaries/1.json
def destroy
#diary.destroy
respond_to do |format|
format.html { redirect_to diaries_url, notice: 'Diary was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_diary
#diary = Diary.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def diary_params
params.require(:diary).permit(:nutrient_id, :amount)
end
end
routes.rb
Rails.application.routes.draw do
resources :diaries
resources :nutrients do
collection do
get :search
end
end
_form.html.erb (for a new diary record)
<%= form_for(#diary) do |f| %>
<% if #diary.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#diary.errors.count, "error") %> prohibited this diary from being saved:</h2>
<ul>
<% #diary.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :nutrient_id %><br>
<%= f.number_field :nutrient_id %>
</div>
<div class="field">
<%= f.label :amount %><br>
<%= f.number_field :amount %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
There are several ways to resolve this, one the quickest and most likely the best approach to me would be to nest the diaries under nutrients, since diaries belongs_to :nutrients
resources :nutrients do
resources :diaries
collection do
get :search
end
end
This way your all your diaries path method would accept a #nutrient argument and your route would be like: /nutrients/4/diaries/1
So in your diaries_controller, you could have:
class DiariesController < ApplicationController
before_action :set_nutrient
def new
#diary = #nutrient.diaries.new
end
def create
#diary = #nutrient.diaries.new(diary_params) # you can safely remove the nutrient_id from the strong params
... remaining logic here
end
...
private
def set_nutrient
#nutrient ||= Nutrient.find(params[:nutrient_id])
end
# You cans skip/ignore this method, if you don't want to be too strict
def set_diary
#diary ||= #nutrient.diaries.find(params[:id])
end
end
Then in your view, you could then have:
<%= form_for([#nutrient, #diary]) do |f| %>
<% if #diary.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#diary.errors.count, "error") %> prohibited this diary from being saved:</h2>
<ul>
<% #diary.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :amount %><br>
<%= f.number_field :amount %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
As I mentioned, there are still other ways of doing this, as you could also use hidden_fields however, this seem to be the cleanest way to me.
If you don't always want your diaries routes to be nested, you could expect: [the list of actions that should not be nested, eg show], both on your before_action and your routes' resources :diaries. Hope I'm able to help or let me know other confusions you may encounter.
I need propagate this values in :departaments_products table: , but I received the error:
I'm using Rails 4
NoMethodError in Products#new
undefined method `departament_id' for #<Product:0x007f916d35d648>
view.html.erb:
<%= form_for(#product) do |f| %>
<% if #product.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#product.errors.count, "error") %> prohibited this product from being saved:</h2>
<ul>
<% #product.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :price %><br>
<%= f.text_field :price %>
</div>
<%= f.collection_select(:departament_id, Departament.all, :id, :name, {:include_blank => true}) %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Products_controller:
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
# GET /products
# GET /products.json
def index
#products = Product.all
end
# GET /products/1
# GET /products/1.json
def show
#product = Product.find( params[:id] )
end
# GET /products/new
def new
#product = Product.new
end
# GET /products/1/edit
def edit
end
# POST /products
# POST /products.json
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Produto criado com sucesso' }
format.json { render :show, status: :created, location: #product }
else
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /products/1
# PATCH/PUT /products/1.json
def update
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to #product, notice: 'Product was successfully updated.' }
format.json { render :show, status: :ok, location: #product }
else
format.html { render :edit }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# DELETE /products/1
# DELETE /products/1.json
def destroy
#product.destroy
respond_to do |format|
format.html { redirect_to products_url, notice: 'Product was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_product
#product = Product.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def product_params
params.require(:product).permit(:name, :price)
end
end
Models:
class Departament < ActiveRecord::Base
has_and_belongs_to_many :products
end
class Product < ActiveRecord::Base
has_and_belongs_to_many :departaments
end
Migration:
class AddProductsAndDepartaments < ActiveRecord::Migration
def change
create_table :departaments_products do |t|
t.references :product, :departament
end
end
end
As its a HABTM association, logically you should be selecting multiple departament_ids for a single product. That said, you should include multiple: true option in the collection_select for departament_ids (Notice departament_ids in plural) in your view code:
<%= f.collection_select(:departament_ids, Departament.all, :id, :name, {include_blank: true}, {multiple: true}) %>
Currently, you are accessing it as departament_id (Notice singular) BUT as per HABTM association you get a method named departament_ids (Notice plural) and NOT departament_id which is why you receive error as NoMethodError in Products#new undefined method 'departament_id'
Once you are done with this change, you need to permit the departament_ids field in ProductsController as below:
def product_params
params.require(:product).permit(:name, :price, :departament_ids => [])
end
:departament_ids => [] is used because multiple selection is allowed for departament_ids and so you would receive it as an Array in params hash upon form submission.
Try departament_ids
For has_many => departament_ids
For has_one => departament_id