Rails 5 error accepts_nested_attributes_for in polymorphic - ruby-on-rails

I have an error when I try create a product: "Cannot build association 'type_data'. Are you trying to build a polymorphic one-to-one association?"
I don't know what is happend. I saw a lot examples with the same code but mine doesn't work.
Here my product.rb
class Product < ApplicationRecord
belongs_to :type_data, polymorphic: true
accepts_nested_attributes_for :type_data, allow_destroy: true
end
Here my hotel_room.rb
class HotelRoom < ApplicationRecord
has_one :product, as: :type_data, dependent: :destroy
has_many :rates
accepts_nested_attributes_for :rates, allow_destroy: true
end
My products/_form.html.erb
<%= f.fields_for :type_data do |builder| %>
<%= render "edit_hotel_account_fields", f: builder %>
<% end %>
My products/_edit_account_fields.heml.erb
<%= f.fields_for :rates do |builder| %>
<%= render 'rate_fields', f: builder %>
<% end %>
<%= link_to_add_fields "Add rate", f, :rates %>
My products/_rate_fields.html.erb
<div class="form-group">
<label><%= t('products.new.rates.double_price') %></label>
<%= f.number_field :double_price, step: 0.1, class: 'form-control' %>
</div>
<%= f.hidden_field :_destroy %>
<%= link_to "remove", '#', class: "remove_fields" %>
And this is all my products_controller.rb
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
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 products_path, notice: 'Product was successfully created.' }
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
# AJAX cargas más fechas
def date_range
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(:status, :provider_id, :commission, :title, :commercial_title, :title_seo, :description, :seo_description, :slug_web, :slug_redirection, :services, :info, :province, :state, :city, :country, :cancel_conditions, :limit_children_age, :special_alert, :notes, :stock, :iva, :minimum_advance, type_data_attributes: [:room_stock, :room_service, :_destroy, rates_attributes: [:title, :start_date, :end_date, :hotel_room_id, :individual_price, :double_price, :child_bed_price, :adult_bed_price, :adult_breakfast_price, :adult_medium_price, :adult_complete_price, :child_breakfast_price, :child_medium_price, :child_complete_price, :_destroy]], dates: {}, map: {}, address_autocomplete: {})
end
end
Do anyone know what is happend?
Thanks!

As Hotel model has one Product via polymorphic,
class HotelRoom < ApplicationRecord
has_one :product, as: :type_data, dependent: :destroy
accepts_nested_attributes_for :product, allow_destroy: true
accepts_nested_attributes_for :rates, allow_destroy: true
....
end
In hotel_rooms controller new action
def new
#hotel_room = HotelRoom.new
#hotel_room.build_product
end
In Hotel view form
<%= form_for(#hotel_room) do |f| %>
....
<%= f.fields_for(:product) do |p| %>
....
<% end %>
<% end %>
Note:
If you have common data between HotelRoom and Product, then you need to introduce another model like TypeData and set it polymorphic.

Related

Rails 5 - how to establish routes for namespaced models

I am trying to learn how to use namespaced models in my Rails 5 app so as to better organise my content.
I have an address model. It is polymorphic. Each of Settings and Organisation have many addresses. Settings is a model which is namespaced under user.
The associations are
User
has_one :setting, dependent: :destroy
Setting
belongs_to :user
has_many :addresses, as: :addressable#, class_name: Address
accepts_nested_attributes_for :addresses, reject_if: :all_blank, allow_destroy: true
Organisation
has_many :addresses, as: :addressable#, class_name: Address
accepts_nested_attributes_for :addresses, reject_if: :all_blank, allow_destroy: true
Address
belongs_to :addressable, :polymorphic => true, optional: true
Routes - organisation
resources :organisations do
namespace :contacts do
resources :addresses
resources :phones
end
end
Routes - setting
resources :users, shallow: true do
scope module: :users do
resources :identities
resources :settings do
namespace :contacts do
resources :addresses
resources :phones
end
end
end
end
Organisation form
<%= f.simple_fields_for :addresses do |f| %>
<%= f.error_notification %>
<%= render 'contacts/addresses/address_fields', f: f %>
<% end %>
<%= link_to_add_association 'Add another address', f, :addresses, partial: 'contacts/addresses/address_fields' %>
user / setting form
<%= simple_form_for [#user, #setting] do |f| %>
<%= f.error_notification %>
<%= simple_fields_for :addresses do |f| %>
<%= f.error_notification %>
<%= render 'contacts/addresses/address_fields', f: f %>
<% end %>
<%= link_to_add_association 'Manage address', f, :addresses, partial: 'contacts/addresses/address_fields' %>
</div>
<% end %>
Everything about my address functionality works fine for Organisation, but I have a problem in getting it to work for my settings.
The problem I have when I try to use the user/setting form to add an address is that I get an error that says:
ActionController::RoutingError at /contacts/addresses/1/edit
uninitialized constant Users::Contacts
There isn't a direct association between user and contacts. Contacts is the name of the namespaced folder I use to store my address views and controller.
Can anyone see what I need to do in order to be able to access the address form functionality from my user settings form?
When I rake routes for setting, I can see the path format to get settings address, but I can't figure out how to use it.
rake routes | grep setting
setting_contacts_addresses GET /settings/:setting_id/contacts/addresses(.:format) users/contacts/addresses#index
POST /settings/:setting_id/contacts/addresses(.:format) users/contacts/addresses#create
new_setting_contacts_address GET /settings/:setting_id/contacts/addresses/new(.:format) users/contacts/addresses#new
SETTINGS CONTROLLER
class Users::SettingsController < ApplicationController
before_action :set_setting, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
after_action :verify_authorized
def index
#settings = Setting.all
authorize #settings
end
def show
# authorize #setting
#addresses = #setting.addresses.all
#phones = #setting.phones
end
def new
#setting = Setting.new
#setting.addresses_build
#setting.phones_build
authorize #setting
end
def edit
#setting.addresses_build unless #setting.addresses
#setting.phones_build unless #setting.phones
end
def create
#setting = Setting.new(setting_params)
authorize #setting
respond_to do |format|
if #setting.save
format.html { redirect_to #setting }
format.json { render :show, status: :created, location: #setting }
else
format.html { render :new }
format.json { render json: #setting.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #setting.update(setting_params)
format.html { redirect_to #setting }
format.json { render :show, status: :ok, location: #setting }
else
format.html { render :edit }
format.json { render json: #setting.errors, status: :unprocessable_entity }
end
end
end
def destroy
#setting.destroy
respond_to do |format|
format.html { redirect_to settings_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_setting
#setting = Setting.find(params[:id])
authorize #setting
end
# Never trust parameters from the scary internet, only allow the white list through.
def setting_params
params.require(:setting).permit( :newsletter,
addresses_attributes: [:id, :description, :unit, :building, :street_number, :street, :city, :region, :zip, :country, :time_zone, :latitude, :longitude, :_destroy],
phones_attributes: [:phone_number, :country, :phone_type],
)
end
end
ORGANISATIONS CONTROLLER
class OrganisationsController < ApplicationController
before_action :set_organisation, only: [:show, :edit, :update, :destroy]
def index
#organisations = Organisation.all
authorize #organisations
end
def show
#addresses = #organisation.addresses.all
# #hash = Gmaps4rails.build_markers(#addresses) do |address, marker|
# marker.lat address.latitude
# marker.lng address.longitude
# marker.infowindow address.full_address
# end
#bips = #organisation.bips
#proposals = #organisation.proposals#.in_state(:publish_openly)
end
def new
#organisation = Organisation.new
#organisation.addresses#_build
end
def edit
#organisation.addresses_build unless #organisation.addresses
end
def create
#organisation = Organisation.new(organisation_params)
respond_to do |format|
if #organisation.save
format.html { redirect_to #organisation, notice: 'Organisation was successfully created.' }
format.json { render :show, status: :created, location: #organisation }
else
format.html { render :new }
format.json { render json: #organisation.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #organisation.update(organisation_params)
format.html { redirect_to #organisation, notice: 'Organisation was successfully updated.' }
format.json { render :show, status: :ok, location: #organisation }
else
format.html { render :edit }
format.json { render json: #organisation.errors, status: :unprocessable_entity }
end
end
end
def destroy
#organisation.destroy
respond_to do |format|
format.html { redirect_to organisations_url, notice: 'Organisation was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_organisation
#organisation = Organisation.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def organisation_params
params.fetch(:organisation, {}).permit(:title, :comment,
addresses_attributes: [:id, :description, :unit, :building, :street_number, :street, :city, :region, :zip, :country, :time_zone, :latitude, :longitude, :_destroy],
phones_attributes: [:id, :phone_number, :country, :phone_type, :_destroy]
)
end
end
I don't quite understand what you're trying to accomplish. These codes below allow you to create a new setting for an user. You can add more addresses to that setting. If you want to edit an address'name, you have to go to the edit page of the setting that address belongs to.
routes.rb
resources :users, shallow: true do
scope module: :users do
resources :settings
end
end
users/settings_controller.rb
class Users::SettingsController < ApplicationController
before_action :prepare_user, only: [:index, :new, :create]
before_action :prepare_setting, only: [:show, :edit, :update]
def new
#setting = Setting.new
end
def create
#setting = #user.build_setting(setting_params)
if #setting.save
redirect_to #setting
else
render 'new'
end
end
def show
end
def edit
end
def update
if #setting.update(setting_params)
redirect_to #setting
else
render 'edit'
end
end
private
def prepare_user
#user = User.find(params[:user_id])
end
def prepare_setting
#setting = Setting.find(params[:id])
end
def setting_params
params.require(:setting).permit(:name, addresses_attributes: [:name, :id])
end
end
users/settings/new.html.erb
<h1>New Setting</h1>
<%= render 'form' %>
users/settings/_form.html.erb
<%= simple_form_for [#user, #setting] do |f| %>
<%= f.input :name %>
<div>
<%= f.simple_fields_for :addresses do |f| %>
<%= f.error_notification %>
<%= render 'contacts/addresses/address_fields', f: f %>
<% end %>
<%= link_to_add_association 'Add another address', f, :addresses, partial: 'contacts/addresses/address_fields' %>
</div>
<%= f.submit %>
<% end %>
contacts/addresses/address_fields.html.erb
<%= f.input :name, label: 'Address name' %>
<%= f.input :id, as: :hidden %>

Nested form working in new but not showing up in edit Rails

I'm making a invoice application where i have the #invoice form and inside it i have nested forms for customers, products, and company information. The products form is working fine in all views but the customer form isn't. When i fill in the customer information and create a new invoice it works. But when i try to edit that invoice the entire form is gone.
invoice/_form
<%= form_for #invoice do |f| %>
<% if #invoice.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#invoice.errors.count, "error") %> prohibited this invoice from being saved:</h2>
<ul>
<% #invoice.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.fields_for :customer do |customer| %>
<div class="field">
<%= customer.label 'Bedrijfsnaam ontvanger' %><br/>
<%= customer.text_field :company_name, placeholder: 'bedrijfsnaam', class: 'form-control' %>
</div>
<div class="field">
<%= customer.label 'Adres ontvanger' %><br>
<%= customer.text_field :address_line_1, placeholder: 'adres ontvanger', class: 'form-control' %>
</div>
<div class="field">
<%= customer.label 'Postcode & stad' %><br>
<%= customer.text_field :zip_code, placeholder: '1234AB Rotterdam', class: 'form-control' %>
</div>
<% end %>
Invoices_controller.rb
class InvoicesController < ApplicationController
before_action :set_invoice, only: [:show, :edit, :update, :destroy]
# GET /invoices
# GET /invoices.json
def index
#invoices = Invoice.all
end
# GET /invoices/1
# GET /invoices/1.json
def show
end
# GET /invoices/new
def new
#invoice = Invoice.new
#invoice.products.build
#invoice.build_customer
end
# GET /invoices/1/edit
def edit
end
# POST /invoices
# POST /invoices.json
def create
#invoice = Invoice.new(invoice_params)
respond_to do |format|
if #invoice.save
format.html { redirect_to #invoice, notice: 'Invoice was successfully created.' }
format.json { render :show, status: :created, location: #invoice }
else
format.html { render :new }
format.json { render json: #invoice.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /invoices/1
# PATCH/PUT /invoices/1.json
def update
respond_to do |format|
if #invoice.update(invoice_params)
format.html { redirect_to #invoice, notice: 'Invoice was successfully updated.' }
format.json { render :show, status: :ok, location: #invoice }
else
format.html { render :edit }
format.json { render json: #invoice.errors, status: :unprocessable_entity }
end
end
end
# DELETE /invoices/1
# DELETE /invoices/1.json
def destroy
#invoice.destroy
respond_to do |format|
format.html { redirect_to invoices_url, notice: 'Invoice was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_invoice
#invoice = Invoice.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def invoice_params
params.require(:invoice).permit(:number, :currency, :date, :duedate, :btwtotal,
:subtotal, :total, :footer, customers_attributes: [:id, :company_name, :address_line_1, :zip_code, :_destroy],
companies_attributes: [:id, :btw_number, :iban_number, :kvk_number, :company_name, :_destroy],
products_attributes: [:id, :quantity, :description, :unitprice, :btw, :total])
end
end
Invoice.rb - (model)
class Invoice < ActiveRecord::Base
has_one :company
has_one :customer
has_many :products
accepts_nested_attributes_for :customer, reject_if: :all_blank, allow_destroy: true
accepts_nested_attributes_for :products, reject_if: :all_blank, allow_destroy: true
validates :number, :currency, :date, :duedate, :btwtotal, :subtotal, :total, presence: true
end
In the invoices controller try changing customers_attributes to customer_attributes in your strong params:
customer_attributes: [:id, :company_name, :address_line_1, :zip_code, :_destroy]
I suspect this is an issue where your customer nested attributes are not being properly saved, so when you go to the edit view that part of the form isn't being rendered because there isn't any customer saved for your invoice

collection_select with multiple models rails 4

I have Products and Books. I'm trying to connect Books to a Product with collection_select
Here's part of my product form:
<%= f.label :book_id %><br>
<%= f.collection_select(:book_id, Book.all, :id, :title, {}, multiple: :true) %>
Book Model:
class Book < ActiveRecord::Base
belongs_to :product
end
Product Model:
class Product < ActiveRecord::Base
has_many :books
end
Migrations:
class AddBookToProducts < ActiveRecord::Migration
def change
add_column :products, :book_id, :integer
add_index :products, :book_id
end
end
class AddProductToBooks < ActiveRecord::Migration
def change
add_column :books, :product_id, :string
add_index :books, :product_id
end
end
BooksController:
def new
#book = Book.new
authorize #book
end
def create
#book = Book.new(book_params)
#book.user = current_user
...
ProductsController:
class ProductsController < ApplicationController
require "stripe"
before_action :set_product, only: [:show, :edit, :update, :destroy]
# GET /products
# GET /products.json
def index
#products = Product.all.page params[:page]
authorize #products
end
# GET /products/1
# GET /products/1.json
def show
#stripe_btn_data = {
key: "#{ Rails.configuration.stripe[:publishable_key] }",
description: #product.title,
amount: (#product.price * 100),
}
authorize #product
end
# GET /products/new
def new
#product = Product.new
#books = Book.all
authorize #product
end
# GET /products/1/edit
def edit
authorize #product
end
# POST /products
# POST /products.json
def create
#product = Product.new(product_params)
authorize #product
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
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
authorize #product
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
authorize #product
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(:title, :body, :price, :active, :state, :hardcopy_book, :online_only, :online_hardcopy, :expiration, :monthly, :annual, :user_id, :book, :book_id, :id)
end
end
Parameters show the :book_id but it does not insert the book_id into products.
Here's how I am showing the selected books on the product/show
<strong>Book:</strong>
<ul>
<% #product.books.each do |book| %>
<li><%= link_to book.tile, (book) %><li>
<% end %>
</ul>
What am I missing?
Firstly, your migration is wrong, we only need one migration and here it is:
class AddBookToProducts < ActiveRecord::Migration
def change
add_column :books, :product_id, :string
add_index :books, :product_id
end
end
Because Book belongs to Product and Product has many Book so add one column product_id to Books table is enough.
Secondly, edit your Product form to: (please note the change from book_id to book_ids)
<%= f.label :book_id %><br>
<%= f.collection_select(:book_ids, Book.all, :id, :title, {}, multiple: :true) %>
Then add book_ids to Strong parameter in ProductsController
def product_params
params.require(:product).permit(:title, :body, :price, :active, :state, :hardcopy_book, :online_only, :online_hardcopy, :expiration, :monthly, :annual, :user_id, :book, :id, :book_ids => [])
end

Rails nested form error: param is missing or the value is empty

I am building an app that allows a user to create a contest. Each contest has many questions and each contests has many entries. Each entry has many answers and each question has many answers. Here are my models:
class Answer < ActiveRecord::Base
belongs_to :entry
belongs_to :question
end
class Contest < ActiveRecord::Base
has_many :entries
has_many :questions
end
class Entry < ActiveRecord::Base
belongs_to :contest
has_many :answers
accepts_nested_attributes_for :answers, allow_destroy: true
end
class Question < ActiveRecord::Base
has_many :answers
belongs_to :contest
end
Everything works except for when I try to create an entry. I get a "param is missing or the value is empty: entry" error. Here is my controller:
class EntriesController < ApplicationController
before_action :set_entry, only: [:show, :edit, :update, :destroy]
before_action :set_contest
# GET /entries
# GET /entries.json
def index
#entries = Entry.all
end
# GET /entries/1
# GET /entries/1.json
def show
end
# GET /entries/new
def new
#entry = Entry.new
end
# GET /entries/1/edit
def edit
end
# POST /entries
# POST /entries.json
def create
#entry = Entry.new(entry_params)
#entry.contest = #contest
respond_to do |format|
if #entry.save
format.html { redirect_to #entry, notice: 'Entry was successfully created.' }
format.json { render :show, status: :created, location: #entry }
else
format.html { render :new }
format.json { render json: #entry.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /entries/1
# PATCH/PUT /entries/1.json
def update
respond_to do |format|
if #entry.update(entry_params)
format.html { redirect_to #entry, notice: 'Entry was successfully updated.' }
format.json { render :show, status: :ok, location: #entry }
else
format.html { render :edit }
format.json { render json: #entry.errors, status: :unprocessable_entity }
end
end
end
# DELETE /entries/1
# DELETE /entries/1.json
def destroy
#entry.destroy
respond_to do |format|
format.html { redirect_to entries_url, notice: 'Entry was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_entry
#entry = Entry.find(params[:id])
end
def set_contest
#contest = Contest.find(params[:contest_id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def entry_params
params.require(:entry).permit(:contest_id, answers_attributes: [:id, :content, :entry_id, :question_id, :_destroy])
end
end
And here is my entry form:
<%= simple_form_for([#contest, #entry]) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<h3>Questions</h3>
<%= simple_fields_for :answers do |ff| %>
<% #contest.questions.each do |question| %>
<h4><%= question.content %></h4>
<%= ff.input :content, input_html: {class: 'form-control'} %>
<% end %>
<% end %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
I am still working on the logic but am perplexed as to why the entry form is giving me this error. Any help would be appreciated!
UPDATE
In the Rails Guide example they show the new action as:
def new
#person = Person.new
2.times { #person.addresses.build}
end
Do I need to build the answer objects in my new action? I'm not sure... I tried it but it didn't work. I feel like that can't be the problem though as the error is coming from the entry_params method
You should be adding this line to your new action.
#entry.answers.build
And change this line
<%= simple_fields_for :answers do |ff| %>
to
<%= f.simple_fields_for :answers do |ff| %>

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

Resources