Rails has_many :through, form with checkboxes not working - ruby-on-rails

I've been going at this for about 5 hours now and have tried just about everything. I'm a front-end dev with limited rails experience so I could just be 100% off base.
Here are my models:
class Apartment < ActiveRecord::Base
validates :name, :amenities, presence: true
has_many :apartment_amenities
has_many :amenities, through: :apartment_amenities
accepts_nested_attributes_for :amenities
end
class Amenity < ActiveRecord::Base
validates :name, presence: true
has_many :apartment_amenities
has_many :apartments, through: :apartment_amenities
end
class ApartmentAmenity < ActiveRecord::Base
belongs_to :apartment
belongs_to :amenity
end
schema:
create_table "amenities", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "apartment_amenities", force: :cascade do |t|
t.integer "apartment_id"
t.integer "amenity_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "apartment_amenities", ["amenity_id"], name: "index_apartment_amenities_on_amenity_id", using: :btree
add_index "apartment_amenities", ["apartment_id"], name: "index_apartment_amenities_on_apartment_id", using: :btree
create_table "apartments", force: :cascade do |t|
t.string "name"
t.string "address"
t.string "website"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Apartment controller:
class Admin::ApartmentsController < AdminController
before_action :set_apartment, only: [:edit, :update, :destroy]
def new
#apartment = Apartment.new
end
def create
#apartment = Apartment.new(apartment_params)
respond_to do |format|
if #apartment.save
format.html { redirect_to apartments_path, notice: 'Apartment was successfully created.' }
format.json { render :show, status: :created, location: #apartment }
else
format.html { render :new }
format.json { render json: #apartment.errors, status: :unprocessable_entity }
end
end
end
private
def set_apartment
#apartment = Apartment.find(params[:id])
end
def apartment_params
params.require(:apartment).permit(:name, :address, :website, amenities_attributes: [:id])
end
end
and last but not least the new apartment form
<%= form_for([:admin, #apartment]) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.collection_check_boxes(:amenities, Amenity.all, :id, :name ) %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Everything looks right when I load the page up and the apartments are saved but the amenities aren't actually getting saved. Thanks for taking a look.

Its because your form fields don't match your strong params. Look at the source code of your form. I suspect you'll find that the n checkboxes look something like
apartment[amenities]
But your strong params has the amenities_attributes as a nested hash. Look at the params hash in the logs and you'll see how the form data is formatted. You'll need to change the form to use a fields_for or change the strong params
params.require(:apartment).permit(:name, :address, :website, amenities: [])

I think you need to permit the name for amenities_attributes as follows:
def apartment_params
params.require(:apartment).permit(:name, :address, :website, amenities_attributes: [:id, :name])
end

Related

Unpermitted parameters In Rails 5 Project

I've spent a few hours trying to solve this problem in a Rails 5 project that I have. The issue is that I keep getting:
Unpermitted parameters: :item_instance_ids, :note_ids
when I submit a form. I believe that the relationships between the models are wrong. I'm using a polymorphic relationship which is the first time I've used it. I've looked through so many posts on StackOverFlow as well as guides on the web but nothing seems to help me.
Basically, I have an incoming purchases form - like an ordering form and within that form you should be able to add multiple items, like a laptop, keyboard, monitor, to the order => the item instances model.
Anyways, here is my code:
incoming_purchases.rb:
class IncomingPurchase < ApplicationRecord
# Relations
has_many :item_instance, :as => :instance_wrapper
has_many :notes, :as => :notable
belongs_to :user
end
item_instance.rb
class ItemInstance < ApplicationRecord
# Relations
belongs_to :instance_wrapper, polymorphic: true
belongs_to :item
belongs_to :user
has_many :notes, :as => :notable
end
views/incoming_purchases/_form.html.erb:
<%= simple_form_for(#incoming_purchase) do |f| %>
<%= f.error_notification %>
<%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>
<div class="form-inputs">
<%= f.association :item_instance, as: :check_boxes, :label_method => lambda { |item_instance| "#{item_instance.item.description}" } %>
<%= f.label(:date_ordered, "Order Date:") %>
<%= f.text_field(:date_ordered, class: 'form-control-date') %>
<%= f.association :user, :label_method => lambda { |user| "#{user.username}" } %>
<%= f.input :order_number %>
<%= f.input :vendor %>
<%= f.input :po_number %>
<%= f.input :tax %>
<%= f.input :shipping %>
<%= f.association :notes %>
</div>
<div class="form-actions">
<%= f.button :submit, class: "btn btn-outline-success" %>
</div>
<% end %>
incoming_puchases_controller.rb:
class IncomingPurchasesController < ApplicationController
before_action :set_incoming_purchase, only: [:show, :edit, :update, :destroy]
def new
#incoming_purchase = IncomingPurchase.new
end
def create
puts '*********************'
puts params
puts '*********************'
puts incoming_purchase_params
puts '**********************'
#incoming_purchase = IncomingPurchase.new(incoming_purchase_params)
respond_to do |format|
if #incoming_purchase.save
format.html { redirect_to #incoming_purchase, notice: 'Incoming purchase was successfully created.' }
format.json { render :show, status: :created, location: #incoming_purchase }
else
format.html { render :new }
format.json { render json: #incoming_purchase.errors, status: :unprocessable_entity }
end
end
end
private
def set_incoming_purchase
#incoming_purchase = IncomingPurchase.find(params[:id])
end
def incoming_purchase_params
params.require(:incoming_purchase).permit(:item_instances_id, :date_ordered, :user_id, :order_number, :vendor, :po_number, :tax, :shipping, :notes_id)
end
end
schema.rb:
ActiveRecord::Schema.define(version: 2020_08_31_200026) do
create_table "incoming_purchases", options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
t.bigint "item_instances_id"
t.date "date_ordered"
t.bigint "user_id"
t.string "order_number"
t.string "vendor"
t.integer "po_number"
t.decimal "tax", precision: 8, scale: 2
t.decimal "shipping", precision: 8, scale: 2
t.bigint "notes_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["item_instances_id"], name: "index_incoming_purchases_on_item_instances_id"
t.index ["notes_id"], name: "index_incoming_purchases_on_notes_id"
t.index ["user_id"], name: "index_incoming_purchases_on_user_id"
end
create_table "item_instances", options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
t.integer "inv_number"
t.string "serial"
t.integer "po_number"
t.date "po_date"
t.date "invoice"
t.date "date_out"
t.decimal "cost", precision: 8, scale: 2
t.string "acro"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "item_id"
t.index ["item_id"], name: "fk_rails_6ea33fd9d0"
end
add_foreign_key "incoming_purchases", "item_instances", column: "item_instances_id"
end
Oh, on the controller I tried:
params.require(:incoming_purchase).permit({ :item_instance_ids => [] }, :date_ordered, :user_id, :order_number, :vendor, :po_number, :tax, :shipping, :notes_id)
Again, I think the problem is how the relationship is set up between these two models. Thank you for any help.
I tried changing my permit params to the following:
params.require(:incoming_purchase).permit(:item_instances_id, :date_ordered, :user_id, :order_number, :vendor, :po_number, :tax, :shipping, notes_id: [], item_instances_id: [])
I was able to add an item but of course item_instances_id did not go through. When the params comes through it looks like this:
{"utf8"=>"✓", "authenticity_token"=>"d3jF73WyKCs69RSCFDvQlh7RyUAg0GQk8m7GKHX6/tt+Ve/1Y1oE5P1UtIMJfCIYS+YL0DwZth9UlDcnyW1uiA==", "incoming_purchase"=>{"item_instance_ids"=>["", "31"], "date_ordered"=>"2020-09-01", "user_id"=>"2", "order_number"=>"1", "vendor"=>"1", "po_number"=>"1", "tax"=>"1", "shipping"=>"1", "note_ids"=>[""]}, "commit"=>"Create Incoming purchase", "controller"=>"incoming_purchases", "action"=>"create"}
notice the item_instance_ids however, on the incoming_purchases model it's
item_instances_id notice the position of that s on ids and instances.
It looks like the filters you are passing into permit are not correct.
It probably needs to be note_ids: [] as this is a has_many relationship.
And when passing nested parameters into permit they should be placed at the end. So, you also have to move item_instance_ids to the end, either before or after note_ids: [].
Edit
You might be better off with a has_many :though relationship for tying items to a purchase. I'm not sure how your Item model looks like so I kept it simple.
incoming_purchase.rb
class IncomingPurchase < ApplicationRecord
has_many :purchase_items
has_many :items, through: :purchase_items
end
purchase_item.rb
class PurchaseItem < ApplicationRecord
belongs_to :incoming_purchase
belongs_to :item
end
item.rb
class Item < ApplicationRecord
has_many :purchase_items
has_many :incoming_purchases, through: :purchase_items
end

How can I save the datas checked with simple_form checkbox?

I am beginner with Ruby on Rails, and I am trying to build a little app that permit people to order a pastrie from a cooker, for a day chosen.
When they select the pastrie using a checkbox, I would like that the selected pastrie was saved as pastrie_id in the table fight but instead, I have an issue :
Validation failed: Pastrie must exist.
UPDATE this is working :
<%= f.association :pastrie, as: :check_boxes, label: 'Pastrie' %>
I still have an issue, the saving params are not good I have :
"fight"=>{"pastrie_id"=>["", "1"]},
I tried a lot of solutions found on stackoverflow but nothing seems to work.
I am using Rails 5.2.3
So, this is my schema :
ActiveRecord::Schema.define(version: 2019_06_06_094318) do
create_table "cookers", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "pastrie"
end
create_table "events", force: :cascade do |t|
t.datetime "date"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "fights", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "pastrie_id"
t.integer "event_id"
t.integer "cooker_id"
t.index ["cooker_id"], name: "index_fights_on_cooker_id"
t.index ["event_id"], name: "index_fights_on_event_id"
t.index ["pastrie_id"], name: "index_fights_on_pastrie_id"
end
create_table "pastries", force: :cascade do |t|
t.string "pastrie_name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
Here is my models :
class Event < ApplicationRecord
has_many :pastries, through: :fights
has_many :cookers, through: :fights
has_many :fights
end
class Fight < ApplicationRecord
belongs_to :pastrie
belongs_to :event
belongs_to :cooker, optional: true
end
class Pastrie < ApplicationRecord
has_many :fights
has_many :events, through: :fights
end
This is my controller. What I understand is : in order to create a Fight, I need an event_id and a pastrie_id (cooker_id is optional). So first, I create a new event (and so I have an event_id), and next, I need to connect a pastrie_id (existing in my seed) to my fight. But this is not working if I am doing that :
class FightsController < ApplicationController
def new
#fight = Fight.new
#event = Event.find(params[:event_id])
#pastries = Pastrie.all
end
def create
#fight = Fight.new(fight_params)
#event = Event.find(params[:event_id])
#fight.event = Event.find(params[:event_id])
#fight.pastrie_id = params[:fight][:pastrie_id]
#fight.save!
redirect_to root_path
end
def show
#events = Event.all
end
def index
#fights = Fight.all
fights_by_event = []
end
private
def fight_params
params.require(:fight).permit(:event_id, :pastrie_id, :cooker_id)
end
end
And my view when I am creating my "fight" :
<div class=margin-bottom>
<h2 class=text-center> Bonjour Linguini, quelles patisseries veux-tu choisir ?</h2>
</div>
<div class="container margin-bottom">
<%= simple_form_for [#event, #fight] do |f| %>
<% #pastries.each do |pastrie| %>
<%= f.label :pastrie_id do %>
<%= f.check_box :pastrie_id, as: :boolean, checked_value: true, unchecked_value: false %> <span><%= pastrie.pastrie_name%></span>
<% end %></br>
<% end %>
<%= f.submit "Valider", class: "btn btn-primary" %>
<% end %>
</div>
And this is my routes if you need this :
Rails.application.routes.draw do
root to: 'pages#home'
resources :events do
resources :fights, only: [:new, :show, :create]
end
resources :fights, only: [:index]
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
In your form change
<%= f.check_box :pastrie_id, as: :boolean, checked_value: true, unchecked_value: false %> <span><%= pastrie.pastrie_name%></span>
To
<%= f.check_box :pastrie_id, as: :boolean, checked_value: pastrie.id, unchecked_value: nil %> <span><%= pastrie.pastrie_name%></span>
In the first version you submit a param of {pastrie_id: true}, which obviously doesn't relate to a Pastry. The second version should submit the ID of the checked box (although if it belongs to only 1 pastry it might make more sense to make these radio buttons)

The nested attributes is not saved in the database on Rails 5

I am having a trouble that I am new with ROR and want to save some images for the organization using nest attributes or for simplicity just a String in order to try the nested attributes saving in the database but actually it is not saved.
Organization Model
class Organization < ApplicationRecord
has_secure_password
has_many :needs, dependent: :destroy
has_many :org_images, dependent: :destroy
has_many :stringas, dependent: :destroy
accepts_nested_attributes_for :org_images, :reject_if => lambda { |t| t['org_image'].blank? }
accepts_nested_attributes_for :stringas, :reject_if => lambda { |t| t['stringa'].blank? }
Schema
create_table "org_images", force: :cascade do |t|
t.string "caption"
t.integer "organization_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "photo_file_name"
t.string "photo_content_type"
t.integer "photo_file_size"
t.datetime "photo_updated_at"
end
create_table "stringas", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "organization_id"
end
Organization Controller
def new
#organization = Organization.new
3.times {#organization.org_images.build}
#organization.stringas.build # added this
end
def organization_params
params.require(:organization).permit(:org_name, :email, :password, :info,:image, :website_URL, :contacts, :logo_url , :password_confirmation ,stringas_attributes:[:name,:id,:organization_id,:created_at,:updated_at] ,org_images_attributes: [:id,:organization_id,:caption, :photo_file_name, :photo_content_type,:photo_file_size,:photo_updated_at])
end
Organization Form
<div class= "field">
<% if builder.object.new_record? %>
<p>
<%= builder.label :caption, "Image Caption" %>
<%= builder.text_field :caption %>
</p>
<p>
<%= builder.label :photo, "Image File" %>
<%= builder.file_field :photo %>
</p>
<% end %>
<% if builder.object.new_record? %>
<p>
<%= builder.label :name %>
<%= builder.text_field :name%>
</p>
<% end %>
<% end %>
Stringa and org_image Models
class OrgImage < ApplicationRecord
belongs_to :organization
has_attached_file :photo, :styles => { :small => "150x150>", :large => "320x240>" }
validates_attachment_presence :photo
validates_attachment_size :photo, :less_than => 5.megabytes
end
class Stringa < ApplicationRecord
belongs_to :organization
end
Organization cotroller create
def create
#organization = Organization.new(organization_params)
respond_to do |format|
if #organization.save
session[:organization_id]=#organization.id
format.html { redirect_to #organization, notice: 'Organization was successfully created.' }
format.json { render :show, status: :created, location: #organization }
else
format.html { render :new }
format.json { render json: #organization.errors, status: :unprocessable_entity }
end
end
end
git repository
Thanks for your help
It seems that problem lies in unpermitted org_images attributes in your OrganizationsController. You should add this to your organization_parameters method:
params.require(:organization).permit( ... , org_images_attributes: [:photo, :caption])
EDIT:
After digging a bit deeper I found out that above solution isn't always working. There's an issue on this topic in Rails repo on GitHub. If you want to find nice workaround that'll suit your needs you should read it, or check out this answer.

Associated model and simple_form in Rails 4

My problem shouldn't be complicated at all but I can't figure out why it isn't working. I've been searching for an answer for many days and tried a lot of things but the problem remains the same, so I apologize if I duplicate question. In my app I have 3 models User, Course & Category.
class Category < ActiveRecord::Base
has_many :courses, inverse_of: :category
end
class Course < ActiveRecord::Base
belongs_to :user
belongs_to :category, inverse_of: :courses
accepts_nested_attributes_for :category
end
The User model has_many :courses
here's the schema for courses and categories:
create_table "categories", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "courses", force: true do |t|
t.string "title"
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
t.integer "category_id"
t.string "address"
t.boolean "domicile"
t.decimal "lat"
t.decimal "lng"
t.string "city"
end
add_index "courses", ["category_id"], name: "index_courses_on_category_id"
add_index "courses", ["user_id"], name: "index_courses_on_user_id"
In my course form I can see the list of categories and I can choose one, but when I create a new course there is no category_id assigned to the course. I use simple_form and here's the category input:
<%= f.association :category, value_method: :id, include_blank: false %>
And in my courses controller there's this :
def create
#course = Course.new(course_params)
#course.user = current_user
respond_to do |format|
if #course.save
format.html { redirect_to #course, notice: 'Course was successfully created.' }
format.json { render :show, status: :created, location: #course }
else
format.html { render :new }
format.json { render json: #course.errors, status: :unprocessable_entity }
end
end
end
And this:
def course_params
params.require(:course).permit(:title, :description, :user_id, :city, :address, :lat, :lng, category_attributes: [:id, :name])
end
I think you don't need accepts_nested_attributes_for :category in your Course model, because it is belongs to one category.
In your controller, your course_params did not permit a category_id params, so new course was not set a category. Your course_params should be:
params.require(:course).permit(:title, :description, :user_id, :city, :address, :lat, :lng, :category_id)
In your form, <%= f.association :category, value_method: :id, include_blank: false %> may be replaced with (to display name of category):
<%= f.association :category, label_method: :name, value_method: :id, include_blank: false %>

Creating a form for a controller inside the show of another controller

I am currently using Rails 4 to create a website and I need to create a form for a model "candidature" inside a model "post" using another model "reponse".
This the post model :
class Post < ActiveRecord::Base
validates :title, presence: true,
length: { minimum: 5 }
belongs_to :entrepreneur
belongs_to :categorie
has_many :candidatures
accepts_nested_attributes_for :candidatures
has_many :questions
accepts_nested_attributes_for :questions,
:reject_if => lambda { |a| a[:enonce].blank? },
:allow_destroy => true
end
The candidature model :
class Candidature < ActiveRecord::Base
has_many :reponses
accepts_nested_attributes_for :reponses, :allow_destroy => true
end
And the reponse model :
class Reponse < ActiveRecord::Base
belongs_to :candidature
end
I don't have controllers for candidature and reponse, because i don't think i need any (do correct me if I'm wrong).
I have created posts that are projects and in the show view of posts, i need to create a form where a guest can answer some questions through answer and all those answers have to be saved in one candidature.
The schema.rb looks like that :
ActiveRecord::Schema.define(version: 20130718083016) do
create_table "candidatures", force: true do |t|
t.datetime "created_at"
t.datetime "updated_at"
t.integer "post_id"
end
create_table "questions", force: true do |t|
t.datetime "created_at"
t.datetime "updated_at"
t.integer "post_id"
t.text "enonce"
end
create_table "reponses", force: true do |t|
t.integer "candidature_id"
t.datetime "created_at"
t.datetime "updated_at"
t.text "enonce"
end
create_table "posts", force: true do |t|
t.string "title"
t.datetime "created_at"
t.datetime "updated_at"
t.text "defi"
t.text "mission"
t.text "competences"
t.text "gain"
t.text "lieny"
t.text "liendb"
t.string "link"
end
The controller for post :
class PostsController < ApplicationController
layout :resolve_layout
include Candidaturetopost
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
flash[:notice] = "Successfully created project."
redirect_to #post
else
render 'new'
end
end
def show
#post = Post.find(params[:id])
#post.candidatures.build
end
def index
#posts = Post.all
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
def destroy
#post = Post.find(params[:id])
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :image, :defi, :mission, :competences, :gain, :lieny, :liendb, :link, questions_attributes: [:enonce, :post_id, :id, :_destroy],
candidatures_attributes: [:post_id, :id, reponses_attributes: [:candidature_id, :id, :enonce]])
end
The show view i have tried to get working :
<%= form_for #post do |f| %>
<%= f.fields_for :candidatures do |cform| %>
<ol>
<% for question in #post.questions %>
<li><%= question.enonce %></li>
<%= cform.fields_for :reponses do |rform| %>
<%= rform.text_area :enonce %>
<% end %>
<% end %>
</ol>
<%= cform.submit %>
<% end %>
<% end %>
With the code shown here, the text_area for enonce isn't even displayed.
Is what I want to do even possible ? How can I have something similar if not ?
Try this
#post.candidatures.build
#post.candidatures.each {|candidature| candidature.responses.build }
But as I wrote in comment you overcomplicated the model structure and I belive you should rethink it.
You were right, it was way too complicated.
I used this to help me get it working, I have now the links I wanted between my three models.

Resources