I am trying to create a many-to-many table relationship in a Rails 5 project.
One Shop can have many Categories, which are indexed in the Shop_Categories table.
I seem to be missing one key step though. When I submit my Shop form, I receive the soft error: "Unpermitted parameter: :shop_category"
I can see my shop_category parameters being successfully sent in the Rails server log, but the Shop_Categories table doesn't get updated at all.
What am I missing, and what can I change so that pressing save on the Shop view form updates the Shop_Categories table?
Here's my code.
Shop Model
class Shop < ApplicationRecord
belongs_to :type
has_many :shop_categories
has_many :categories, through: :shop_categories
accepts_nested_attributes_for :shop_categories
enum status: [:open, :closed, :opening_soon, :closing_soon]
end
Shop_Category Model
class ShopCategory < ApplicationRecord
belongs_to :shop
belongs_to :category
end
Category Model
class Category < ApplicationRecord
has_many :subcategories, dependent: :destroy
has_many :shop_categories
has_many :shops, through: :shop_categories
end
Shop Controller (Excerpt)
def new
#shop = Shop.new
#shop.shop_categories.build
end
def create
#shop = Shop.new(shop_params)
#shop.open_date = params[:shop][:open_date]+"-01"
#shop.close_date = params[:shop][:close_date]+"-01"
if #shop.save
redirect_to #shop
else
render 'new'
end
end
def update
#shop = Shop.find(params[:id])
if #shop.update(shop_params)
redirect_to #shop
else
render 'edit'
end
end
private
def shop_params
params[:shop][:open_date] = params[:shop][:open_date]+"-01"
params[:shop][:close_date] = params[:shop][:close_date]+"-01"
params.require(:shop).permit(:shop_name, :type_id, :status,
:phone_number, :mobile_number, :email_address, :open_date,
:close_date, shop_categories_attributes: [ :shop_id, :category_id] )
end
Shop Form View (Excerpt)
<p>
<%= form.label :shop_name %><br>
<%= form.text_field :shop_name %>
</p>
<%= form.fields_for :shop_category do |category_fields| %>
<p>
<%= category_fields.label :category %><br />
<%= category_fields.collection_select(:category_id, Category.all, :id, :category_name, include_blank: true) %>
</p>
<% end %>
<p>
<%= form.submit %>
</p>
And finally, the Database Schema
create_table "categories", force: :cascade do |t|
t.string "category_name"
t.text "category_description"
t.string "visible_category"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "shop_categories", force: :cascade do |t|
t.string "visible_category_override"
t.integer "shop_id"
t.integer "category_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["category_id"], name: "index_shop_categories_on_category_id"
t.index ["shop_id"], name: "index_shop_categories_on_shop_id"
end
create_table "shops", force: :cascade do |t|
t.string "shop_name"
t.integer "status"
t.integer "sale"
t.string "phone_number"
t.string "mobile_number"
t.string "email_address"
t.date "open_date"
t.date "close_date"
t.integer "type_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["type_id"], name: "index_shops_on_type_id"
end
Any suggestions would be greatly appreciated. I just can't work out which piece of the puzzle I'm missing!
Update: Including the Dev log, and the full Shop Form.
Dev Log
Started PATCH "/shops/1" for 127.0.0.1 at 2018-01-14 11:08:39 +0000
Processing by ShopsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"2qydDj9JovMrg9VT6Lo5xbcNSHl5MH0ylmq3IMMX4iRTAVSgK6JlXWpG+CyuwQK6svZJn9wtnEnZANUlOZYzXA==",
"shop"=>{"shop_name"=>"test", "type_id"=>"1",
"shop_categories"=>{"category_id"=>"1"}, "subcategory_id"=>"",
"status"=>"open", "phone_number"=>"123", "mobile_number"=>"13212",
"email_address"=>"hello#test.com", "open_date"=>"2017-12",
"close_date"=>"2017-12"}, "commit"=>"Update Shop", "id"=>"1"}
Shop Load (0.5ms) SELECT "shops".* FROM "shops" WHERE "shops"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
Unpermitted parameters: :shop_categories, :subcategory_id
(0.1ms) begin transaction
Type Load (0.2ms) SELECT "types".* FROM "types" WHERE "types"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
(0.1ms) commit transaction
Redirected to http://localhost:3000/shops/1
Completed 302 Found in 10ms (ActiveRecord: 0.9ms)
Full view/shops/_form.html.erb file
<%= form_with model: #shop, local: true do |form| %>
<% if #shop.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#shop.errors.count, "error") %> prohibited
this shop from being saved:
</h2>
<ul>
<% #shop.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= form.label :shop_name %><br>
<%= form.text_field :shop_name %>
</p>
<p>
<%= form.label :type %><br />
<%= form.collection_select(:type_id, Type.all, :id, :type_name) %>
</p>
<%= form.fields_for :shop_categories do |category_fields| %>
<p>
<%= category_fields.label :category %><br />
<%= category_fields.collection_select(:category_id, Category.all, :id, :category_name, include_blank: true) %>
</p>
<% end %>
<p>
<%= form.label :subcategory %><br />
<%= form.grouped_collection_select :subcategory_id, Category.all, :subcategories, :category_name, :id, :visible_subcategory, include_blank: true %>
</p>
<p>
<%= form.label :status %><br />
<%= form.select :status, Shop.statuses.keys %>
</p>
<p>
<%= form.label :phone_number %><br>
<%= form.telephone_field :phone_number %>
</p>
<p>
<%= form.label :mobile_number %><br>
<%= form.telephone_field :mobile_number %>
</p>
<p>
<%= form.label :email_address %><br>
<%= form.email_field :email_address %>
</p>
<p>
<%= form.label :open_date %><br>
<%= form.month_field :open_date %>
</p>
<p>
<%= form.label :close_date %><br>
<%= form.month_field :close_date %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
It should probably be :shop_categories, not :shop_category
<%= f.fields_for :shop_categories do |category_f| %>
...
<% end %>
You also kind of lost me in the Shop_Category Model part. The actual class name is different (ShopSubcategory) and it belongs_to a subcategory? This will probably cause some errors as well. I guess what you meant is the ShopCategoryjoin model and it belongs_to :category
Update
Also be sure to build your nested resource when creating a new form:
#shop_controller
def new
#shop = Shop.new
#shop.shop_categories.build
...
end
Related
I'm currently trying to display the organization which a content identifies themselves with via a collection_select form.
The issue is that after creating a new contact and choosing an organization. The organization doesn't display.
I have tried searching the web for an answer so far but nothing has helped. I have also tried everything that I can think of is the issue with my code.
Here are my models:
class Contact < ApplicationRecord
has_many :contact_orgs
has_many :organizations, through: :contact_orgs
accepts_nested_attributes_for :organizations
end
class Organization < ApplicationRecord
has_many :contact_orgs
has_many :contacts, through: :contact_orgs
end
class ContactOrg < ApplicationRecord
belongs_to :contact
belongs_to :organization
accepts_nested_attributes_for :organization
end
Here is my schema.rb:
create_table "contact_orgs", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "contact_id"
t.integer "organization_id"
t.index ["contact_id"], name: "index_contact_orgs_on_contact_id"
t.index ["organization_id"], name: "index_contact_orgs_on_organization_id"
end
create_table "contacts", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "organizations", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "industry"
end
Here is my contact_controller.rb:
private
def contact_params
params.require(:contact).permit(:first_name, :last_name,
organizations_attributes: [:name, :industry])
end
def new
#contact = Contact.new
end
def show
#contact = Contact.find(params[:id])
end
def create
#contact = Contact.new(contact_params)
if #contact.save
redirect_to #contact
else
render 'new'
end
end
Here is my contact/new.html.erb:
<%= form_with scope: :contact, url: contact_path, local: true do |form| %>
<p>
<%= form.label :first_name %><br>
<%= form.text_field :first_name %>
</p>
<p>
<%= form.label :last_name %><br>
<%= form.text_field :last_name %>
</p>
<p>
<%= form.label :organization_id, "Organization:" %><br>
<%= form.collection_select :organization_id, Organization.order(:name), :id, :name, {}, {multiple: true} %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
Here is my contact/show.html.erb:
<p>
<strong>First name:</strong>
<%= #contact.first_name %>
</p>
<p>
<strong>Last Name:</strong>
<%= #contact.last_name %>
</p>
<hr>
<p>
<strong>Organizations:</strong>
<ul>
<% #contact.organizations.each do |organization| %>
<li><%= organization.name %></li>
<% end %>
</ul>
</p>
Here is what my Rails Server is saying when I refresh my localhost:3000/contact/2 page.
Here is what is in my Rails Console:
2.4.1 :001 > Contact.find(2).organizations
Contact Load (0.2ms) SELECT "contacts".* FROM "contacts" WHERE "contacts"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
Organization Load (0.2ms) SELECT "organizations".* FROM "organizations" INNER JOIN "contact_orgs" ON "organizations"."id" = "contact_orgs"."organization_id" WHERE "contact_orgs"."contact_id" = ? LIMIT ? [["contact_id", 2], ["LIMIT", 11]]=> #<ActiveRecord::Associations::CollectionProxy []>
Here is my routes.rb file:
Rails.application.routes.draw do
get 'welcome/index'
resources :contacts, :organizations
root 'welcome#index'
end
Thank you ahead of time :)
Edit:
Added Contacts#Show method and Contact.find(2).organization via Rails c command.
Added routes.rb
Since you’re using a joins table (ContactOrg) you really are trying to create a new ContactOrg record when you create a Contact, not assign it to Organization, which is how your code reads.
EDIT
While my first statement above still has a (little) bit of merit (you are in fact creating a joins record), you can definitely let rails help you out and you were pretty close in your original answer. Here's code only where there are updates, I tried to highlight changes.
app/models/contact.rb
class Contact < ApplicationRecord
has_many :contact_orgs
has_many :organizations, through: :contact_orgs
accepts_nested_attributes_for :organizations # you were correct here
end
app/views/contact/new.html.erb
<%= form_with model: :contact, local: true do |form| %>
# use :model here so you can ultimately use this form for both new and edit.
# The model method will infer the correct path
... contact fields here as you have them ...
<p>
<%= form.collection_select :organization_ids, Organization.order(:name), :id, :name, {}, {multiple: true} %>
# the attribute for organization_id should be plural because you're accepting multiple
<p>
<p>
<%= form.submit %>
</p>
<% end %>
app/controllers/contacts_controller.rb
class ContactsController < ApplicationController
... new, show, and create as you have them ...
private # private methods should go after all public methods
def contact_params
params.require(:contact).permit(:first_name, :last_name, organization_ids: [])
# again, the attribute you're passing in is called organization_ids, and you give it an empty array
end
end
Your contract_controller do not initialize #contact used in show.html.erb because there isn't any show method does it that.
Try to add in contact_controller
def show
#contact = Contact.find(param[:id])
end
PS: when you call show method be sure to pass to it a contact.id
I hope this help you.
There are two problems:
You are not able to save the organizations to your contract.
You can check in your rails c by Contact.find(2).organizations.
We never use to write new and create in the private method.
Your private method should not contain any of the curd.
In your app/controllers/contacts_controller.rb
private
def contact_params
params.require(:contact).permit(
:first_name,
:last_name,
organizations_attributes: [
:name,
:industry
]
)
end
In your app/views/contact/new.html.erb
<%= form_with scope: :contact, url: contact_index_path, local: true do |form| %>
<p>
<%= form.label :first_name %><br>
<%= form.text_field :first_name %>
</p>
<p>
<%= form.label :last_name %><br>
<%= form.text_field :last_name %>
</p>
<p>
<%= form.fields_for :organizations do |o| %>
<%= o.label :organization %><br>
<%= o.collection_select(:organization_id, Organization.all,
:id, :org_name,
{:prompt => 'Please select the organization'}) %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
I am building an application to manage debts and am trying to create three objects using the same form.
The models are;
Debt (the amount owed)
class Debt < ActiveRecord::Base
belongs_to :user
belongs_to :creditor
belongs_to :debtor
accepts_nested_attributes_for :debtor, :creditor
end
Debtor (The person who owes)
class Debtor < ActiveRecord::Base
belongs_to :user
has_many :debts
end
Creditor (The person who is owed)
class Creditor < ActiveRecord::Base
belongs_to :user
has_many :debts
end
I am using the new debt form and would like to show fields for debtor and creditor. So when a new debt is created, so is the associated debtor and creditor.
I have viewed the documentation however, cannot get the fields for debtor or creditor to display on the form.
This is the form
<%= simple_form_for(#debt) do |f| %>
<%= f.error_notification %>
<!-- Debt fields -->
<div class="form-inputs">
<%= f.association :user %>
<%= f.input :amount %>
<%= f.input :commission %>
<%= f.input :invoice_issued %>
<%= f.input :invoice_date %>
<%= f.input :status %>
<%= f.input :details %>
</div>
<!-- Debtor Fields -->
<ul>
<%= f.fields_for :debtor do |debtor_form| %>
<li>
<%= debtor_form.association :user %>
<%= debtor_form.input :business_name %>
<%= debtor_form.input :abn %>
<%= debtor_form.input :first_name %>
<%= debtor_form.input :last_name %>
<%= debtor_form.input :email %>
<%= debtor_form.input :mobile_number %>
<%= debtor_form.input :phone_number %>
</li>
<% end %>
</ul>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
Any help is appreciated.
EDIT
create_table "debts", force: :cascade do |t|
t.integer "user_id"
t.float "amount"
t.float "commission"
t.boolean "invoice_issued"
t.date "invoice_date"
t.string "status"
t.text "details"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
You need to build your debt's debtor or creditor in your controller:
#controller
def new
#debt = Debt.new
#debt.build_debtor
#bebt.build_creditor
end
Using Rails 4 and Ruby 2.2,
I have book as model which should have image upload functionality in the show page of book. So User can create the book first and from show page upload the multiple images for book. I have used carrierwave as gem and have separate model created for Image.
image.rb
class Image < ActiveRecord::Base
belongs_to :book
mount_uploader :avatar, AvatarUploader
end
book.rb
class Book < ActiveRecord::Base
belongs_to :subject, dependent: :destroy
belongs_to :user, dependent: :destroy
has_many :images, dependent: :destroy
end
books/show.html.erb
<%= form_for(Image.new, :html => { :multipart => true }) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
<%= hidden_field_tag "image[book_id]", #book.id %>
</div>
<%= f.fields_for :images do |p| %>
<div class="field">
<%= p.label :avatar %><br>
<%= p.file_field :avatar, :multiple => true, name: "images[avatar]" %>
</div>
<%end%>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
schema.rb
create_table "images", force: :cascade do |t|
t.datetime "avatar_updated_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "book_id", limit: 4
t.string "avatar", limit: 255
t.string "name", limit: 255
end
create_table "books", force: :cascade do |t|
t.string "title", limit: 255
t.integer "page_number", limit: 4
t.text "description", limit: 65535
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id", limit: 4
t.integer "subject_id", limit: 4
t.boolean "active", default: false
end
Now I am kind of unable to proceed with this, can someone guide me on this because I am having form in books/show page and I need to show the image on the same page after successful updation of images.(Multiple images can be uploaded)
Thanks in advance
let me know if I need to provide any more information.
You can use the association itself and list all the images which have been uploaded.
<%= form_for(Image.new, :html => { :multipart => true }) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
<%= hidden_field_tag "image[book_id]", #book.id %>
</div>
<%= f.fields_for :images do |p| %>
<div class="field">
<%= p.label :avatar %><br>
<%= p.file_field :avatar, :multiple => true, name: "images[avatar]" %>
</div>
<% end %>
<!-- Listing all the images for a book -->
<% if #book.images.any? %>
<% #book.images.each do |img| %>
<%= image_tag(img.avatar.url) %>
<% end %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I've searched a lot , found similar questions , but any seem to fit , or i did not understood right.
i have 3 models , i choose a bad name for one model, that is for Tv Shows , and you may confuse the Show model with show method and view .
Show.rb
class Show < ActiveRecord::Base
has_many :seasons
belongs_to :user
has_many :episodes , through: :seasons
accepts_nested_attributes_for :seasons
attr_accessible :name,:status,:duration
end
Season.rb
class Season < ActiveRecord::Base
belongs_to :show
has_many :episodes
accepts_nested_attributes_for :episodes
attr_accessible :order,:status
end
Episode.rb
class Episode < ActiveRecord::Base
belongs_to :season
attr_accessible :number
end
My forms for each model are separeted, what i wanna do is set the foreigns keys for each one automatically. So , in the view of my Show i wanna create a season that will be stored in that Show.
Shows/show.html.erb
p id="notice"><%= notice %></p>
<% #seasons = Season.all %>
<% #episodes= Episode.all%>
<p>
<strong>Name:</strong>
<%= #show.name %>
</p>
<p>
<strong>Status:</strong>
<%= #show.status %>
</p>
<p>
<strong>Episode Duration:</strong>
<%= #show.Episode_Duration %>
</p>
<% #seasons.where(:show_id => #show.id).each do |season| %>
<tr>
<td><%= season.order %></td>
<td><%= season.status %></td>
<h3>episodios</h3>
<hr>
<% #episodes.where(:season_id => season.id).each do |episode| %>
<td> <%= episode.number%></td>
<%end%>
<hr>
<td><%= link_to 'episode', episode_new_path(season.id)%></td>
</tr>
<br>
<% end %>
<h1><%=#show.id%></h1>
<h1><%=#show.episodes.count%></h1>
<h1></h1>
**<%= link_to 'create seasons', season_new_path(#show.id) %>**
<%= link_to 'Edit', edit_show_path(#show) %> |
<%= link_to 'Back', shows_path %>
In that lasts line, i'm passing #show.id for create a season, i want use that #show.id as my foreign key :show_id for that season. how i use that id in my params?
I saw some posts and i tried this , but didn't worked , heres the controller(only the part that i think is useful.)
seasons_controller.rb
def create
#season = Season.new(season_params)
#season.show_id = params[:id]
#season.save
end
respond_to do |format|
if #season.save
format.html { redirect_to #season, notice: 'Season was successfully created.' }
format.json { render :show, status: :created, location: #season }
else
format.html { render :new }
format.json { render json: #season.errors, status: :unprocessable_entity }
end
end
def season_params
params.require(:season).permit(:order, :status)
end
EDIT
My routes are:
Routes.rb
devise_for :users
resources :episodes
resources :seasons
resources :shows
root 'shows#index'
get 'seasons/create/:id' => 'seasons#new', as: :season_new
get 'episodes/create/:id' => 'episodes#new', as: :episode_new
put 'subscribe_show/:id' => 'shows#subscribe', as: :user_subscribe_show
put 'unsubscribe_show/:id' => 'shows#unsubscribe', as: :user_unsubscribe_show
get 'user/dashboard/' => 'users#index', as: :user_dashboard
And my forms are:
seasons/form.html.erb
<%= form_for(#season) do |f| %>
<% if #season.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#season.errors.count, "error") %> prohibited this season from being saved:</h2>
<ul>
<% #season.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :order %><br>
<%= f.number_field :order %>
</div>
<div class="field">
<%= f.label :status %><br>
<%= f.text_field :status %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Show/form.html.erb
<%= form_for(#show) do |f| %>
<% if #show.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#show.errors.count, "error") %> prohibited this show from being saved:</h2>
<ul>
<% #show.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 :status %><br>
<%= f.text_field :status %>
</div>
<div class="field">
<%= f.label :Episode_Duration %><br>
<%= f.text_field :Episode_Duration %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
EDIT 2:
schema.rb
create_table "episodes", force: :cascade do |t|
t.integer "number"
t.integer "season_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "seasons", force: :cascade do |t|
t.integer "order"
t.string "status"
t.integer "show_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "watched"
end
create_table "shows", force: :cascade do |t|
t.string "name"
t.string "status"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "Episode_Duration"
t.integer "user_id"
end
Migrations
class AddUserIdtoShow < ActiveRecord::Migration
def change
add_index :shows , :user_id
end
end
Im new to ror so sorry if that's a really basic question.
So, in my app i have quizzes and each quiz, beside questions and answers, should have 5 links.
class Quiz < ActiveRecord::Base
has_and_belongs_to_many :users
has_many :questions
has_many :links
end
class Link < ActiveRecord::Base
belongs_to :quizzes
end
Schema looks like this:
create_table "links", force: true do |t|
t.string "name"
t.string "www"
t.integer "quiz_id"
end
create_table "questions", force: true do |t|
t.string "content"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "quiz_id"
end
create_table "quizzes", force: true do |t|
t.string "content"
t.datetime "created_at"
t.datetime "updated_at"
end
Now, in views I want to add links as i Add/Edit my quiz.
View for form looks like this:
<%= form_for(#quiz) do |f| %>
<% if #quiz.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#quiz.errors.count, "error") %> prohibited this quiz from being saved:</h2>
<ul>
<% #quiz.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :content %><br>
<%= f.text_field :content %>
<%= **THIS IS PLACE FOR TEXT FIELD FOR LINK_NAME AND LINK_WWW** %>
</div>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
How do I create a form to add the links?
You can use fields_for:
<%= f.label :content %><br>
<%= f.text_field :content %>
<%= f.fields_for #quiz.links.new do |ff| %>
<%= ff.label :name %>
<%= ff.text_field :name %>
<% end %>
And allow the Quiz to update the Link records associated:
class Quiz < ActiveRecord::Base
accepts_nested_attributes_for :links