Rails 4 simple_form has_many through check boxes not saving - ruby-on-rails

I am trying to implement an employee recurrent deductions table through check boxes using simple_form. My code works but the selected recurrent deductions are not saved in my table. I can't work out why.
Here are my models.
class Employee < ActiveRecord::Base
belongs_to :club
has_many :employee_recurrents
has_many :recurrents, :through => :employee_recurrents
end
class Recurrent < ActiveRecord::Base
belongs_to :club
has_many :employee_recurrents
has_many :employees, :through => :employee_recurrents
end
class EmployeeRecurrent < ActiveRecord::Base
belongs_to :employee
belongs_to :recurrent
end
migration
class CreateEmployeeRecurrents < ActiveRecord::Migration
def change
create_table :employee_recurrents, :id => false do |t|
t.references :employee
t.references :recurrent
end
add_index :employee_recurrents, [:employee_id, :recurrent_id]
add_index :employee_recurrents, [:recurrent_id, :employee_id]
end
end
form
<%= simple_form_for #employee, html: {class: 'form-horizontal' } do |f| %>
<%= f.error_notification %>
<%= f.input :first_name %>
<%= f.input :last_name %>
<%= f.association :recurrents,
as: :check_boxes,
label_method: :description,
value_method: :id,
label: 'Recurrent Deductions' %>
<%= f.button :submit, class: 'btn btn-primary' %>
<% end %>
controller
def employee_params
params.require(:employee).permit(:first_name, :recurrent_ids)
end
here is my edited log
Started PATCH "/employees/1" for 127.0.0.1 at 2013-08-12 19:38:42 +0800
Processing by EmployeesController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"IwWzcT2qQJqHkTumWjb3OD5nJe9xLaA+YezMumer9X8=", "employee"=>{"job_id"=>"5", "manager_id"=>"", "first_name"=>"Mark", "recurrent_ids"=>["1", ""]}, "commit"=>"Update Employee", "id"=>"1"}
[1m[36mActiveRecord::SessionStore::Session Load (1.2ms)[0m [1mSELECT "sessions".* FROM "sessions" WHERE "sessions"."session_id" = '9a8b066a4033f8fa2ed867371ffaa2a3' ORDER BY "sessions"."id" ASC LIMIT 1[0m
[1m[35mUser Load (0.5ms)[0m SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
[1m[36mEmployee Load (0.8ms)[0m [1mSELECT "employees".* FROM "employees" WHERE "employees"."id" = $1 ORDER BY last_name, first_name LIMIT 1[0m [["id", "1"]]
Unpermitted parameters: recurrent_ids
[1m[35m (0.6ms)[0m BEGIN
[1m[36mEmployee Exists (1.1ms)[0m [1mSELECT 1 AS one FROM "employees" WHERE ("employees"."email" = 'mark#yahoo.com' AND "employees"."id" != 1 AND "employees"."club_id" = 1) LIMIT 1[0m
[1m[35m (0.4ms)[0m COMMIT
Redirected to http://localhost:3000/employees
Completed 302 Found in 22ms (ActiveRecord: 4.6ms)

I needed the following strong params
def employee_params
params.require(:employee).permit(:first_name, :recurrent_ids => [])
end

Related

Unpermitted parameter on has_one and belongs_to

I'm trying to update a #user.profile (with differents solutions) but get unpermitted parameters or Profile User must exist.
But in my erb console :
[3] pry(main)> User.first.id
User Load (3.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1]]
=> 1
[4] pry(main)> User.first.profile.id
User Load (2.8ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1]]
Profile Load (2.2ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."user_id" = $1 LIMIT $2 [["user_id", 1], ["LIMIT", 1]]
=> 4352
What's wrong ??
{"_method"=>"patch", ....
"user"=>{"id"=>"1", "profile_attributes"=>{"user_id"=>"1", "last_name"=>"test", "id"=>"4352"}}, "commit"=>"Update", "locale"=>"fr", "controller"=>"users", "action"=>"update", "id"=>"ben"}
users_controller.rb
class UsersController < ApplicationController
def edit
#user = User.find(params[:id]
end
def update
#user = User.find(params[:id]
if #user.update_without_password(user_params)
bypass_sign_in(#user)
redirect_to edit_user_path(#user.slug), notice: t('users.user_updated')
else
flash.alert = "#{#user.errors.full_messages.join(', ')}"
render :edit
end
end
private
def user_params
params.require(:user).permit (
:id,
:email,
:pseudo,
:password,
:password_confirmation,
:current_password,
profile_attributes: %i[
id
user_id]
)
end
end
user.rb
class User < ApplicationRecord
has_one :profile, inverse_of: :user, dependent: :destroy
accepts_nested_attributes_for :profile
end
profile.rb
class Profile < ApplicationRecord
belongs_to :user, inverse_of: :profile
accepts_nested_attributes_for :user
attr_accessor :user
end
form
<%= simple_form_for #user do |f| %>
<%= f.error_notification %>
<%= f.input :id, input_html: { value: #user.id } %>
<%= f.simple_fields_for :profile do |profile| %>
<%= profile.input :user_id, input_html: { value: #user.id } %>
<%= profile.input :id, input_html: { value: #user.profile.id } %>
<% end %>
<%= f.button :submit %>
<% end %>

Double nested form not sending attributes to controller

All help/hints/debugging tips/thoughts are welcome, as I'm pretty much stuck for some time on this issue.
I have a 2-level deep nested form. The parameters of the 1st level are saving correctly (e.g. options_attributes), but unfortunately the parameters of the deepest form are not being sent to the controller (.e.g. option_prices_attributes is not shown at all in my parameters. I use the cocoon gem to create a dynamic nester form.
Interestingly,
(1) in my console I am able to create a 2-level deep object where also the option_price parameters are saving.
(2) when using :option_prices_attributes in the simple_field forms, they are being sent as parameters:
{"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"P6oCZkZF8O+, "accommodation_category"=>{"options_attributes"=>{"1568305809712"=>{"name"=>"Option name", "_destroy"=>"false", "option_prices_attributes"=>{"name"=>"Option price", "_destroy"=>"0"}}}}, "commit"=>"Save", "park_id"=>"8", "id"=>"96"}
=> and consequently resulting in an error message in the terminal saying
no implicit conversion of Symbol into Integer
My models
class AccommodationCategory < ApplicationRecord
belongs_to :park
has_many :options, dependent: :destroy
accepts_nested_attributes_for :options, allow_destroy: true
end
class Option < ApplicationRecord
belongs_to :accommodation_category
has_many :option_prices, dependent: :destroy
accepts_nested_attributes_for :option_prices, allow_destroy: true
end
class OptionPrice < ApplicationRecord
belongs_to :option
end
Accommodation_categories_controller.rb
class AccommodationCategoriesController < ApplicationController
# skip_before_action :authenticate_user!
[...]
def update
#park = Park.find(params[:park_id])
#accommodation_category = #park.accommodation_categories.find(params[:id])
authorize #accommodation_category
#accommodation_category = #accommodation_category.update_attributes(accommodation_category_params)
end
def new_options
#accommodation_category = AccommodationCategory.find(params[:id])
#park = #accommodation_category.park
authorize #accommodation_category
#2nd level nesting
# #accommodation_category.options.build
#accommodation_category.options.each do |option|
option.option_prices.build
end
end
private
def accommodation_category_params
params.require(:accommodation_category).permit(:name, :description, :status, :persons_max, :persons_min, :persons_included, :accommodation_count, :enabled_accommodation_count, :thumb, :included_services, :photo,
options_attributes: [:name, :description, :_destroy,
option_prices_attributes: [:name, :price_type, :start_date, :end_date, :price, :duration, :duration_min, :duration_max, :backend_only, :weekend_extra, :_destroy]])
end
end
views/accommodation_categories/new_options.html.erb
<%= render 'options_new_form', park: #park%>
views/accommodation_categories/options_new_form.html.erb (1st level)
<%= simple_form_for [#park, #accommodation_category] do |f|%>
<h1> <%= #accommodation_category.name %> </h1>
<% #accommodation_category.options.each do |option| %>
<%= option %>
<% end %>
<%= f.simple_fields_for :options do |option| %>
<%= render 'option_fields', f: option %>
<% end %>
<div>
<%= link_to_add_association 'add option', f, :options %>
</div>
<%= f.submit "Save", class: "btn btn-primary" %>
<% end %>
views/accommodation_categories/option_fields.html.erb (2nd level)
<%= f.input :name %>
<%= f.check_box :_destroy %>
<%= link_to_remove_association "remove option", f %>
<%= f.simple_fields_for :option_prices do |option_price| %>
<%= render 'option_price_fields', f: option_price %>
<% end %>
<%= link_to_add_association 'add option price', f, :option_prices %>
views/accommodation_categories/option_price_fields.html.erb
<%= f.input :name %>
<%= f.check_box :_destroy %>
<%= link_to_remove_association "remove option price", f %>
The message in my terminal when sending the parameter to the controller is the following:
Processing by AccommodationCategoriesController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"QlROXu9ImP6GSvPJQgd7eVZtaWsiVT6myWzZEFIEtEulSrQmt75XVMEI/avKUzhjZaZG9Kj0Pmih6J/4UYO8IQ==", "accommodation_category"=>{"options_attributes"=>{"1568290804865"=>{"name"=>"option name", "_destroy"=>"false"}}}, "commit"=>"Save", "park_id"=>"8", "id"=>"93"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /Users/xx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98
Park Load (0.4ms) SELECT "parks".* FROM "parks" WHERE "parks"."id" = $1 LIMIT $2 [["id", 8], ["LIMIT", 1]]
↳ app/controllers/accommodation_categories_controller.rb:29
AccommodationCategory Load (0.2ms) SELECT "accommodation_categories".* FROM "accommodation_categories" WHERE "accommodation_categories"."park_id" = $1 AND "accommodation_categories"."id" = $2 LIMIT $3 [["park_id", 8], ["id", 93], ["LIMIT", 1]]
↳ app/controllers/accommodation_categories_controller.rb:30
(0.2ms) BEGIN
↳ app/controllers/accommodation_categories_controller.rb:32
Option Create (0.3ms) INSERT INTO "options" ("accommodation_category_id", "name", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["accommodation_category_id", 93], ["name", "option name"], ["created_at", "2019-09-12 12:20:14.265335"], ["updated_at", "2019-09-12 12:20:14.265335"]]
↳ app/controllers/accommodation_categories_controller.rb:32
(0.7ms) COMMIT
↳ app/controllers/accommodation_categories_controller.rb:32
AccommodationCategory Load (0.2ms) SELECT "accommodation_categories".* FROM "accommodation_categories" WHERE "accommodation_categories"."park_id" = $1 AND "accommodation_categories"."id" = $2 LIMIT $3 [["park_id", 8], ["id", 93], ["LIMIT", 1]]
↳ app/controllers/accommodation_categories_controller.rb:45
Redirected to http://localhost:3000/accommodation_categories/93/new_discounts
Completed 302 Found in 14ms (ActiveRecord: 2.3ms)
None of your nested-fields partials seem to have a wrapper-class? Cocoon explicitly relies on a specific mark-up, this could cause e.g. the nested fields to appear to be inserted correctly, but inserted outside of the form and then of course never posted to the server/controller.

Rails unpermitted parameter but model creation still working

I keep getting an unpermitted parameter error, but the model creation is still working. Adding or removing :active_comment_relationship in the strong parameters makes no difference. What's wrong with the code?
notices_controller.rb:
#notice = #character.notices.build(notice_params)
if #notice.save
.
.
def notice_params
params.require(:notice).permit( :content, :picture, :latitude, :longitude, # :active_comment_relationship, # adding or removing :active_comment_relationship makes no difference
active_comment_relationship_attributes: [:commentee_id] )
end
notice.rb:
has_one :active_comment_relationship, class_name: "Commentrelationship",
foreign_key: "commenter_id",
dependent: :destroy
has_one :supernotice, through: :active_comment_relationship, source: :commentee
accepts_nested_attributes_for :active_comment_relationship
_notice_form.html.erb:
<%= form_for(:notice, url: :notices, method: :post, multiple: true, remote: true ) do |f| %>
<%= hidden_field_tag :callsign, #character.callsign %>
<%= f.fields_for :active_comment_relationship do |ff| %>
<%= ff.hidden_field :commentee_id, value: nil %>
<% end %>
.
.
<% end %>
logs:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"UoJ...ieQ==",
"callsign"=>"bazzer",
"notice"=>{
"active_comment_relationship"=>{"commentee_id"=>""},
"latitude"=>"54.00292618287952",
"longitude"=>"-1.1480712890625",
"content"=>"Hello."},
"Drop"=>""}
Character Load (1.0ms) SELECT "characters".* FROM "characters" WHERE "characters"."callsign" = $1 LIMIT 1 [["callsign", "bazzer"]]
User Load (1.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 1]]
CACHE (0.1ms) SELECT "characters".* FROM "characters" WHERE "characters"."callsign" = $1 LIMIT 1 [["callsign", "bazzer"]]
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 1]]
Unpermitted parameter: active_comment_relationship

Creating association with existing model using has_many :through

I'd like users to be able to create a User System with multiple existing Parts. That it. Currently when I edit or create a user system with a part select I get.
Processing by UserSystemsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"LgudsjWoAROXi0DGpUaH0ie2GNAuooRGtsPN+fOg+Gtev6Q6QmJC4dYIwFW9evaBOa0TUWotrQec1o1ROCbwFQ==", "user_system"=>{"name"=>"adfa"}, "parts_user_systems"=>{"part_id"=>["", "2", "3"]}, "button"=>""}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["id", 101]]
(0.1ms) BEGIN
SQL (0.6ms) INSERT INTO "user_systems" ("name", "user_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["name", "adfa"], ["user_id", 101], ["created_at", "2015-07-10 17:10:39.564555"], ["updated_at", "2015-07-10 17:10:39.564555"]]
(0.2ms) COMMIT
Nothing get's submitted to the join table.
class UserSystem < ActiveRecord::Base
has_many :parts_user_systems, foreign_key: "part_id"
has_many :parts, through: :parts_user_systems, foreign_key: "part_id"
end
class Part < ActiveRecord::Base
has_many :parts_user_systems
has_many :user_systems, through: :parts_user_systems
end
class PartsUserSystem < ActiveRecord::Base
belongs_to :part
belongs_to :user_system
end
class UserSystemsController < ApplicationController
def user_system_params
params.require(:user_system).permit(:name, :user_id, part_ids: [:ids])
end
end
_form.html.erb
<%= form_for(#user_system) do |f| %>
<div class="field">
<%= f.label :part_id, "Part " %><br/>
<%= collection_select :parts_user_systems, :part_id, Part.all, :id, :name, {:selected => 1}, {:multiple => true} %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
</div>
<% end %>
show.html.erb
<%= #user_system.parts.each do |part| %>
<li><%= part.name%> by by <%= part.parts_user_system%></li></li>
<% end %>

Rails4: To check for no image uploaded with carrierwave is not working when delete the image

What I want to do is for users to select at least one image (up to 3 images) and to enter f.text_area :content in \views\shared\ _article_form.html.erb.
I added a custom validation check_for_at_least_image in \models\article.rb.
It works (error message is displayed) only in create action, but it doesn't work in update action.
How can I check no image and display error message in update action.
article has many photo.
The logs are as followings.
\log/development.log
no image when new create (error message was displayed as I expect)
Started POST "/articles" for 127.0.0.1 at 2014-09-13 10:40:49 +0900
Processing by ArticlesController#create as HTML
Parameters: {"utf8"=>"笨・, "authenticity_token"=>"xxxx=", "article"=>{"category_id"=>"1379", "photos_attributes"=>{"0"=>{"article_id"=>""}, "1"=>{"article_id"=>""}, "2"=>{"article_id"=>""}}, "content"=>"test"}, "commit"=>"逋サ骭イ縺吶k"}
[1m[35mUser Load (0.0ms)[0m SELECT "users".* FROM "users" WHERE "users"."remember_token" = 'xxxx' LIMIT 1
[1m[36m (0.0ms)[0m [1mbegin transaction[0m
[1m[35m (0.0ms)[0m rollback transaction
[1m[36mCategory Load (1.0ms)[0m [1mSELECT "categories".* FROM "categories" WHERE "categories"."id" = ? LIMIT 1[0m [["id", 1379]]
Rendered shared/_error_messages.html.erb (1.0ms)
Rendered shared/_article_form.html.erb (8.0ms)
Rendered articles/new.html.erb within layouts/application (10.0ms)
Rendered layouts/_header.html.erb (1.0ms)
Rendered layouts/_footer.html.erb (0.0ms)
Completed 200 OK in 63ms (Views: 51.0ms | ActiveRecord: 1.0ms)
delete all(three) images (no error message was displayed)
Started PATCH "/articles/40" for 127.0.0.1 at 2014-09-13 11:10:00 +0900
Processing by ArticlesController#update as HTML
Parameters: {"utf8"=>"笨・, "authenticity_token"=>"xxxx=", "article"=>{"category_id"=>"1379", "photos_attributes"=>{"0"=>{"article_id"=>"40", "_destroy"=>"1", "id"=>"132"}, "1"=>{"article_id"=>"40", "_destroy"=>"1", "id"=>"133"}, "2"=>{"article_id"=>"40", "_destroy"=>"1", "id"=>"134"}}, "content"=>"test"}, "commit"=>"譖エ譁ー縺吶k", "id"=>"40"}
[1m[35mUser Load (0.0ms)[0m SELECT "users".* FROM "users" WHERE "users"."remember_token" = 'xxxx' LIMIT 1
[1m[36mArticle Load (0.0ms)[0m [1mSELECT "articles".* FROM "articles" WHERE "articles"."user_id" = ? AND "articles"."id" = 40 ORDER BY created_at DESC LIMIT 1[0m [["user_id", 1]]
[1m[35mArticle Load (1.0ms)[0m SELECT "articles".* FROM "articles" WHERE "articles"."id" = ? ORDER BY created_at DESC LIMIT 1 [["id", "40"]]
[1m[36m (0.0ms)[0m [1mbegin transaction[0m
[1m[35mPhoto Load (1.0ms)[0m SELECT "photos".* FROM "photos" WHERE "photos"."article_id" = ? AND "photos"."id" IN (132, 133, 134) [["article_id", 40]]
[1m[36m (0.0ms)[0m [1mSELECT COUNT(*) FROM "photos" WHERE "photos"."article_id" = ?[0m [["article_id", 40]]
[1m[35mSQL (1.0ms)[0m DELETE FROM "photos" WHERE "photos"."id" = ? [["id", 132]]
[1m[36mSQL (0.0ms)[0m [1mDELETE FROM "photos" WHERE "photos"."id" = ?[0m [["id", 133]]
[1m[35mSQL (0.0ms)[0m DELETE FROM "photos" WHERE "photos"."id" = ? [["id", 134]]
[1m[36m (4.0ms)[0m [1mcommit transaction[0m
Redirected to http://localhost:3000/users/1
Completed 302 Found in 26ms (ActiveRecord: 7.0ms)
photos table
sqlite> .schema photos
CREATE TABLE "photos" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "articl_id" integer, "image" varchar(255), "created_at" datetime, "updated_at" datetim);
\models\article.rb
# encoding: utf-8
class Article < ActiveRecord::Base
belongs_to :user
belongs_to :category
has_many :photos, dependent: :destroy
accepts_nested_attributes_for :photos, reject_if: :all_blank, allow_destroy: true
default_scope -> { order('created_at DESC') }
validates :content, presence: true, length: { maximum: 140 }
validates :user_id, presence: true
validates :category_id, presence: true
validate :check_for_at_least_image
def build_images
(3 - self.photos.size).times {self.photos.build}
end
def check_for_at_least_image
errors.add(:image, "select...") if self.photos.size <= 0
end
end
\models\photo.rb
class Photo < ActiveRecord::Base
belongs_to :article
mount_uploader :image, ImageUploader
end
\view\articles\edit.html.erb
<div class="row">
<div class="span8">
<%= render 'shared/article_form' %>
</div>
</div>
\view\shared\ _article_form.html.erb
<%= form_for(#article) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.hidden_field :category_id %>
<%= f.fields_for :photos do |p| %>
<%= p.hidden_field :article_id %>
<div class="photo">
<% if p.object.image and p.object.image.file %>
<%= image_tag p.object.image.thumb.url %>
<p>article:<%= #article.id %></p>
<p>photo:<%= p.object.id %></p>
<%= p.hidden_field :image_cache if p.object.image_cache %>
<label><%= p.check_box :_destroy %>delete</label>
<% end %>
<%= p.file_field :image %>
</div>
<% end %>
<%= f.text_area :content, placeholder: "Enter content..." %>
</div>
<%= f.submit class: "btn btn-large btn-primary" %>
<% end %>
\controllers\articles_controller.rb
class ArticlesController < ApplicationController
before_action :signed_in_user, only: [:create, :destroy]
before_action :correct_user, only: [:update, :destroy]
.
.
def new
#article = Article.new
#category = Category.find(params[:category])
#article.category_id = #category.id
3.times { #article.photos.build }
end
def create
#article = current_user.articles.build(article_params)
if #article.save
flash[:success] = "article created!"
redirect_to current_user #root_url
else
#article.build_images
render 'new'
end
end
.
.
def edit
#article = Article.find(params[:id])
#article.build_images
end
def update
#article = Article.find(params[:id])
if #article.update(article_params)
redirect_to current_user
else
render 'edit'
end
end
def destroy
#article.destroy
redirect_to root_url
end
private
def article_params
params.require(:article).permit(:content, :category_id, photos_attributes: [:id, :article_id, :image, :image_cache, :_destroy])
end
.
.
end
Just looked at your question and was thinking that may be your validations are running before the destory call on child objects and searched around a bit and found this post, looks like i was right about validations being run before destory. Just going to post pointer related to your question
The problem here is that accepts_nested_attributes_for call destroy for child objects AFTER validation of the parent object. So the user is able to delete an image. Of course, later, when the user will try to edit an article, he/she will get an error – “Select at least one image.”.
Fix:
accepts_nested_attributes_for :photos, reject_if: proc { |attributes| attributes['image'].blank? } , allow_destroy: true #as discussed in your other question you have to use proc to solve your update problem
validate :check_for_at_least_image
def check_for_at_least_image
errors.add(:image, "select...") if photos.reject(&:marked_for_destruction?).size <= 0
end

Resources