Difficulties with Nested Forms in Rails 6.1.3.2 - ruby-on-rails

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.

Related

Find record in table and save id to another table in Rails

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.

Rails 4 Nested Model : Not working

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.

Nested Form in Rails 4 not being rendered in view

Apologies if this has already been answered but I can't find anything that can help me. I am a newbie with Rails so please be gentle :D
I have been pulling my hair out trying to get nested forms working, I am sure I got nested forms working using Rails 3 and the railscasts demo last year, but Rails 4 is beating me.
Looking at the log, the query is being run to pull the data for the associated table, but nothing is rendered in the form.
I have read many web sites, but none have helped so far and I don't know where to start. The latest article I have followed is this http://www.createdbypete.com/articles/working-with-nested-forms-and-a-many-to-many-association-in-rails-4/
Still nothing being rendered in the view.
Where do I start debugging this, maybe my Rails install is broken?? But I am probably missing something crucial.
Thanks,
Royce
Edit - I have added some of the controllers and the view in question
surveys_controller.rb
class SurveysController < ApplicationController
before_action :set_survey, only: [:show, :edit, :update, :destroy, :answers]
# GET /surveys
# GET /surveys.json
def index
#surveys = Survey.all
end
# GET /surveys/1
# GET /surveys/1.json
def show
end
# GET /surveys/new
def new
#survey = Survey.new
end
# GET /surveys/1/edit
def edit
end
# POST /surveys
# POST /surveys.json
def create
#survey = Survey.new(survey_params)
respond_to do |format|
if #survey.save
format.html { redirect_to #survey, notice: 'Survey was successfully created.' }
format.json { render :show, status: :created, location: #survey }
else
format.html { render :new }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /surveys/1
# PATCH/PUT /surveys/1.json
def update
respond_to do |format|
if #survey.update(survey_params)
format.html { redirect_to #survey, notice: 'Survey was successfully updated.' }
format.json { render :show, status: :ok, location: #survey }
else
format.html { render :edit }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
# DELETE /surveys/1
# DELETE /surveys/1.json
def destroy
#survey.destroy
respond_to do |format|
format.html { redirect_to surveys_url, notice: 'Survey was successfully destroyed.' }
format.json { head :no_content }
end
end
def answers
#participants = Participant.all
#questions = #survey.questions
end
private
# Use callbacks to share common setup or constraints between actions.
def set_survey
#survey = Survey.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def survey_params
params.require(:survey).permit(:name,
:questions_attributes => [:id, :content,
:answers_attributes => [:id, :content, :participant_id]
])
end
end
participents_controller.rb
class ParticipantsController < ApplicationController
before_action :set_participant, only: [:show, :edit, :update, :destroy]
# GET /participants
# GET /participants.json
def index
#participants = Participant.all
end
# GET /participants/1
# GET /participants/1.json
def show
end
# GET /participants/new
def new
#participant = Participant.new
end
# GET /participants/1/edit
def edit
end
# POST /participants
# POST /participants.json
def create
#participant = Participant.new(participant_params)
respond_to do |format|
if #participant.save
format.html { redirect_to #participant, notice: 'Participant was successfully created.' }
format.json { render :show, status: :created, location: #participant }
else
format.html { render :new }
format.json { render json: #participant.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /participants/1
# PATCH/PUT /participants/1.json
def update
respond_to do |format|
if #participant.update(participant_params)
format.html { redirect_to #participant, notice: 'Participant was successfully updated.' }
format.json { render :show, status: :ok, location: #participant }
else
format.html { render :edit }
format.json { render json: #participant.errors, status: :unprocessable_entity }
end
end
end
# DELETE /participants/1
# DELETE /participants/1.json
def destroy
#participant.destroy
respond_to do |format|
format.html { redirect_to participants_url, notice: 'Participant was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_participant
#participant = Participant.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def participant_params
params.require(:participant).permit(:name)
end
end
application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
end
answers.html.erb
<h1><%= #survey.name %> Answers</h1>
<%= form_for(#survey) do |f| %>
<% #participants.each do |participant| -%>
<h3><%= participant.name %></h3>
<table>
<thead>
<tr>
<td>Questions</td>
<td>Answer</td>
</tr>
</thead>
<tbody>
<% #questions.each do |question| -%>
<tr>
<td><%= question.content %></td>
<td>
<%= f.fields_for :questions, question do |q| -%>
<%= q.fields_for :answers, question.answers.find_or_initialize_by(participant: participant) do |a| -%>
<%= a.text_area :content %>
<%= a.hidden_field :participant_id, participant.id %>
<% end -%>
<% end -%>
</td>
</tr>
<% end -%>
</tbody>
</table>
<% end -%>
<div class="actions">
<%= f.submit %>
</div>
<% end -%>
As you're new with Rails, let me explain how nested forms work for you!
--
Nested
Nested forms are not actually nested at all - they are associative forms.
You must remember that Rails (by virtue of being built on Ruby) is an object orientated framework. OOP (object orientated programming) is not just a buzzword - it's a fundamental core construction for your application & how it hands input / execution.
The problem many people have is they don't realize the true nature of Rails, and consequently become confused about how its many features work. If you appreciate that everything you do in Rails should be constructed around objects, life gets much simpler!
--
Form
With this in mind, you can begin to appreciate the role of objects throughout Rails, to the degree that you need to build / invoke objects for every element of your Rails application, including your form:
#app/models/survey.rb
Class Survey < ActiveRecord::Base
has_many :questions
accepts_nested_attributes_for :questions
end
#app/controllers/surveys_controller.rb
Class SurveysController < ApplicationController
def new
#survey = Survey.new
#survey.questions.build #-> very important
end
end
#app/views/surveys/new.html.erb
<%= form_for #survey do |f| %>
...
<%= f.fields_for :questions do |q| %>
<%= q.text_field :title %>
<% end %>
<%= f.submit %>
<% end %>
This should create a form which allows you to pass associative data through to your child model. There are several important elements to consider:
You need to include accepts_nested_attributes_for in your "parent" model
You need to build your associative objects
You need to populate your form with the relative objects
By following this simple pattern, you'll be able to populate the nested form that you wish to show in the view
Try to use the following code:
<%= f.fields_for :questions do |q| -%>
<%= q.fields_for :answers, q.object.answers.find_or_initialize_by(participant: f.object.participant) do |a| -%>
<%= a.text_area :content %>
<%= a.hidden_field :participant_id, participant.id %>
<% end -%>
<% end -%>
and make sure that you render to answers.html.erb, you have accepts_nested_attributes_for :questions in survey.rb file, and accepts_nested_attributes_for :answers in question.rb file
Have you got accepts_nested_attributes_for :question in your survey model? And the same for the answer model?

HABTM Association Build

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

NoMethodError in Statuses#new

I'm currently building this website in college for fun. My hope is that it will help the education system. Anyways, I'm still trying to figure out rails. I just set up the devise gem with no problem. However, when I click post a new status it gives me this error:
NoMethodError in Statuses#new
Showing /Users/wyatt/Network/netbook/app/views/statuses/_form.html.erb where line #16 raised:
undefined method `user_name' for #<Status:0x00000101ec36d0>
Extracted source (around line #16):
13:
14: <div class="field">
15: <%= f.label :user_name %><br />
16: <%= f.text_field :user_name %>
17: </div>
18: <div class="field">
19: <%= f.label :content %><br />
So here's what my form looks like:
<%= form_for(#status) do |f| %>
<% if #status.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#status.errors.count, "error") %> prohibited this status from being saved:</h2>
<ul>
<% #status.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :user_name %><br />
<%= f.text_field :user_name %>
</div>
<div class="field">
<%= f.label :content %><br />
<%= f.text_area :content %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Here's my controller
class StatusesController < ApplicationController
# GET /statuses
# GET /statuses.json
def index
#statuses = Status.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #statuses }
end
end
# GET /statuses/1
# GET /statuses/1.json
def show
#status = Status.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #status }
end
end
# GET /statuses/new
# GET /statuses/new.json
def new
#status = Status.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #status }
end
end
# GET /statuses/1/edit
def edit
#status = Status.find(params[:id])
end
# POST /statuses
# POST /statuses.json
def create
#status = Status.new(params[:status])
respond_to do |format|
if #status.save
format.html { redirect_to #status, notice: 'Status was successfully created.' }
format.json { render json: #status, status: :created, location: #status }
else
format.html { render action: "new" }
format.json { render json: #status.errors, status: :unprocessable_entity }
end
end
end
# PUT /statuses/1
# PUT /statuses/1.json
def update
#status = Status.find(params[:id])
respond_to do |format|
if #status.update_attributes(params[:status])
format.html { redirect_to #status, notice: 'Status was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #status.errors, status: :unprocessable_entity }
end
end
end
# DELETE /statuses/1
# DELETE /statuses/1.json
def destroy
#status = Status.find(params[:id])
#status.destroy
respond_to do |format|
format.html { redirect_to statuses_url }
format.json { head :no_content }
end
end
end
From looking at it, it seems you're wanting to append a user_name to a status
Error
Your error basically means you don't have a user_name column in your statuses data table. The basic fix will be to create a migration to add a user_name column to your statuses db:
$ rails generate migration AddUserNameToStatuses
#db/migrations/AddUserNameToStatuses.rb
class AddUserNameToStatuses < ActiveRecord::Migration
def change
add_column :statuses, :user_name, :string
end
end
$ rake db:migrate
Fix
I would actually ditch that, and do this:
#app/models/status.rb
Class Status < ActiveRecord::Base
belongs_to :user #-> you need user_id column in statuses db
delegate :name, to: :user, prefix: true
end
#app/models/user.rb
Class User < ActiveRecord::Base
has_many :statuses
end
This will allow you to remove the references to user_name from your form - as any #status will be associated with a user
You'd then be able to use the .delegate() method to call user_name in statuses

Resources