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 %>
Related
CONTEXT
I'm using devise_invitable to allow a user (with an admin role) to register another user in my app.
User objects have an email, password, token (random string), role (also string) and an associated HealthRecord object which has name, last name, dni (personal id) plus some extra info
PROBLEM
For some reason, when I input an existing email, I get an error (which is intended validation) but it also destroys the HealthRecord associated with the user who has that existing email.
CODE
This is what my console shows upon trying to create the user with existing email
Started POST "/users/invitation" for ::1 at 2021-11-26 10:04:15 -0300
Processing by Users::InvitationsController#create as HTML
Parameters: {"authenticity_token"=>"[FILTERED]", "user"=>{"email"=>"paciente1#example.com", "role"=>"Paciente", "health_record_attributes"=>{"residencia"=>"Cementerio", "nombre"=>"overriding", "apellido"=>"test", "dni"=>"123456789", "risk"=>"0", "birth"=>"1999-02-12"}}, "commit"=>"Registrar"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 5], ["LIMIT", 1]]
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."email" = ? ORDER BY "users"."id" ASC LIMIT ? [["email", "paciente1#example.com"], ["LIMIT", 1]]
HealthRecord Load (0.1ms) SELECT "health_records".* FROM "health_records" WHERE "health_records"."user_id" = ? LIMIT ? [["user_id", 1], ["LIMIT", 1]]
TRANSACTION (0.1ms) begin transaction
HealthRecord Destroy (0.5ms) DELETE FROM "health_records" WHERE "health_records"."id" = ? [["id", 1]]
TRANSACTION (207.2ms) commit transaction
User Exists? (0.3ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = ? AND "users"."id" != ? LIMIT ? [["email", "paciente1#example.com"], ["id", 1], ["LIMIT", 1]]
HealthRecord Exists? (0.4ms) SELECT 1 AS one FROM "health_records" WHERE "health_records"."dni" = ? LIMIT ? [["dni", "123456789"], ["LIMIT", 1]]
Rendering layout layouts/application.html.erb
Rendering users/invitations/new.html.erb within layouts/application
HealthRecord Load (0.1ms) SELECT "health_records".* FROM "health_records" WHERE "health_records"."user_id" = ? LIMIT ? [["user_id", 5], ["LIMIT", 1]]
↳ app/views/users/invitations/new.html.erb:18
The view to generate the new user
<h2>Registro excepcional</h2>
<%= form_for(setup_user(resource), as: resource_name, url: invitation_path(resource_name), html: { method: :post }) do |f| %>
<% resource.class.invite_key_fields.each do |field| -%>
<div class="field">
<%= f.label field %><br />
<%= f.text_field field, class: 'form-control'%>
</div>
<% end %>
<div class="field">
<%= f.hidden_field :role, :value=>"Paciente"%>
</div>
<%= f.fields_for :health_record do |ff| %>
<div class="field">
<%= ff.hidden_field :residencia, :value=>current_user.health_record.residencia%>
</div>
<div class="field">
<%= ff.label "Nombre" %><br/>
<%= ff.text_field :nombre, class: 'form-control',:required => true%>
</div>
<div class="field">
<%= ff.label "Apellido" %><br/>
<%= ff.text_field :apellido, class: 'form-control',:required => true%>
</div>
<div class="field">
<%= ff.label "DNI" %><br/>
<%= ff.text_field :dni, class: 'form-control',:required => true%>
</div>
<div class="field">
<%= ff.label "Es de riesgo:",:required => true %>
<%= ff.check_box :risk %>
</div>
<div class="field">
<%= ff.label "Fecha de nacimiento:"%><br/>
<%= ff.date_field :birth, class: 'form-control',:required => true%>
</div>
<% end %>
<br/>
<div class="actions">
<%= f.submit "Registrar" %>
</div>
<% end %>
The user model which has validate_on_invite
class User < ApplicationRecord
devise :invitable, :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :authentication_keys => [:token,:email], :validate_on_invite => true
has_many :comprobantes, :dependent => :destroy
has_one :health_record, :dependent => :destroy
has_many :TurnoAsignado, :dependent => :destroy
has_many :TurnoNoAsignado, :dependent => :destroy
validates :email, uniqueness: true
before_save :init
accepts_nested_attributes_for :health_record
def init()
if self.token.nil?
self.token = (rand()*1000000).to_i
end
end
end
The HealthRecord model
class HealthRecord < ApplicationRecord
belongs_to :user
validates :dni, presence: true
validates :dni, uniqueness: true
validates :nombre, presence: true
validates :apellido, presence: true
validates :birth, presence: true
before_save :upcase_content
def upcase_content
self.nombre=self.nombre.downcase
self.apellido=self.apellido.downcase
self.nombre=self.nombre.split(/ |\_/).map(&:capitalize).join(" ")
self.apellido=self.apellido.split(/ |\_/).map(&:capitalize).join(" ")
end
end
The invitation controller (it's pretty much default I just added parameters and an after_path)
class Users::InvitationsController < Devise::InvitationsController
before_action :configure_permitted_parameters
#Permit the new params here.
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:invite, keys: [
:token,
:role,
health_record_attributes: [
:apellido,
:nombre,
:dni,
:risk,
:birth,
:residencia
]
])
end
def after_invite_path_for(resource)
new_asignado_path(self.resource.id)
end
end
I think the problem may have been this line
form_for(setup_user(resource),...
I am using a helper to set the HealthRecord of a user to an empty one (fields_for needed the user to have a HealthRecord to work)
module FormHelper
def setup_user(user)
user.health_record ||= HealthRecord.new # ||= means “assign this value unless it already has a value”
user
end
end
Maybe what was happening is that the empty HealthRecord was assigned to the existing user, somehow?
I solved it by intercepting the flow of the create method in the invitations controller, by asking if the user email or dni exists
def create
#correo = User.find_by(email:params[:user][:email])
#dni = HealthRecord.find_by(dni:params[:user][:health_record_attributes][:dni])
if (#correo.nil? && #dni.nil?) #si no existe mail ni dni
super
else
mensaje="Los siguientes campos ya estan registrados:"
if !(#correo.nil?)
mensaje = mensaje + " email"
end
if !(#dni.nil?)
mensaje = mensaje + " dni"
end
flash[:notice] = mensaje
redirect_to new_user_invitation_path
end
end
Although this works, I'm not sure about the reason of the problem, any insight is welcome
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.
I'm not sure why but I cannot save my form input to the database, and I couldn't find any answer so far :), but it works for creating the posts using the console.
Started POST "/posts" for 127.0.0.1 at 2018-05-23 14:04:32 +0300
Processing by PostsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"B19yVi1jHtg7068SKFbI3tj28THNdfgGYwUSN+e79Vt/o0ivDUl0D4i71PuLKlZf1wxnEJMVnp+GmH/HcxE6cQ==", "post"=>{"category_id"=>"2", "title"=>"asfaf", "content"=>"asfkja", "photo_cache"=>""}, "commit"=>"Create Post"}
Unpermitted parameter: :photo_cache
User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.5ms) BEGIN
Category Load (0.5ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
(0.4ms) ROLLBACK
<%= simple_form_for(#post) do |f| %>
<%= f.collection_select(:category_id, Category.all, :id, :name, {prompt: "Choose a category" }) %><br>
<%= f.label :title %><br>
<%= f.text_field :title, placeholder: "Type the post title here" %><br>
<%= f.label :content %><br>
<%= f.text_area :content, placeholder: "Type the post text here" %><br>
<%= f.input :photo %>
<%= f.input :photo_cache, as: :hidden %><br>
<%= f.submit %>
<% end %>
def new
#post = Post.new
authorize #post
end
def create
#post = Post.new(post_params)
authorize #post
if #post.save
redirect_to #post, notice: "The post was created!"
else
render 'new'
end
end
def post_params
params.require(:post).permit(:title, :content, :category_id, :photo, :user_id)
end
class Post < ApplicationRecord
validates :title, :content, :category_id, presence: true
belongs_to :category
belongs_to :user
mount_uploader :photo, PhotoUploader
end
Console output points the reason - you pass photo_cache parameter while have not permitted it:
Unpermitted parameter: :photo_cache
So, just add :photo_cache to permit list:
params.require(:post).permit(..., :photo_cache)
UPDATE: If Post model belongs to User, it should have foreign key constraint and you must pass user_id parameter to Post.create.
In real apps something like Devise is used for authentication and controller looks like:
class PostsController < ApplicationController
before_action :authenticate_user!
def create
post = current_user.posts.create!(create_params)
# post = Post.new(create_params.merge(user: current_user))
# ...
end
end
Basically what helped is changing the create method
def create
#post = current_user.posts.new(post_params)
authorize #post
if #post.save
redirect_to #post, notice: "The post was created!"
else
render 'new'
end
end
When I delete a image which was uploaded using carrierwave, a new record is inserted at the same time.
How can I avoid inserting a new record?
article has many photo.
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);
The id 93 and 94 exist before deleting photo.
sqlite> select * from photos;
93|27|DSCN0722_070.JPG|2014-09-07 01:59:03.320092|2014-09-07 01:59:03.320092
94|27|DSCN0725_070.JPG|2014-09-07 01:59:03.332093|2014-09-07 01:59:03.332093
When I checked check box to delete id=93 and submitted, id=93 is deleted I expected but a new record id=95 is inserted with no image, I'm not sure.
sqlite> select * from photos;
94|27|DSCN0725_070.JPG|2014-09-07 01:59:03.332093|2014-09-07
95|27||2014-09-07 02:01:58.634119|2014-09-07 02:01:58.634119
If there are 3 records, it works (a new record isn't inserted).
But there are 2 records like above, it occurs.
Thanks in advance.
log/development.log
.
.
Started PATCH "/articles/27" for 127.0.0.1 at 2014-09-07 11:01:58 +0900
Processing by ArticlesController#update as HTML
Parameters: {"utf8"=>"笨・, "authenticity_token"=>"xxx=", "article"=>{"category_id"=>"1379", "photos_attributes"=>{"0"=>{"article_id"=>"27", "_destroy"=>"1", "id"=>"93"}, "1"=>{"article_id"=>"27", "_destroy"=>"0", "id"=>"94"}, "2"=>{"article_id"=>"27"}}, "content"=>"test"}, "commit"=>"譖エ譁ー縺吶k", "id"=>"27"}
[1m[35mUser Load (1.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" = 27 ORDER BY created_at DESC LIMIT 1[0m [["user_id", 1]]
[1m[35mArticle Load (0.0ms)[0m SELECT "articles".* FROM "articles" WHERE "articles"."id" = ? ORDER BY created_at DESC LIMIT 1 [["id", "27"]]
[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 (93, 94) [["article_id", 27]]
[1m[36m (0.0ms)[0m [1mSELECT COUNT(*) FROM "photos" WHERE "photos"."article_id" = ?[0m [["article_id", 27]]
#Delete id=93
[1m[35mSQL (0.0ms)[0m DELETE FROM "photos" WHERE "photos"."id" = ? [["id", 93]]
#Why new record is inserted?
[1m[36mSQL (0.0ms)[0m [1mINSERT INTO "photos" ("article_id", "created_at", "updated_at") VALUES (?, ?, ?)[0m [["article_id", 27], ["created_at", Sun, 07 Sep 2014 02:01:58 UTC +00:00], ["updated_at", Sun, 07 Sep 2014 02:01:58 UTC +00:00]]
[1m[35m (5.0ms)[0m commit transaction
Redirected to http://localhost:3000/users/1
Completed 302 Found in 48ms (ActiveRecord: 7.0ms)
\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
If you look at your logs attributes for other image are still passed to the update action when you are deleting a image
"article"=>{"category_id"=>"1379", "photos_attributes"=>{"0"=>{"article_id"=>"27", "_destroy"=>"1", "id"=>"93"}, "1"=>{"article_id"=>"27", "_destroy"=>"0", "id"=>"94"}, "2"=>{"article_id"=>"27"}}, "content"=>"test"}, "commit"=>"譖エ譁ー縺吶k", "id"=>"27"}
FIX
You have two options:
a. Don't build a new image in edit action:.
You are building a new image in your edit action which is then passed on to your update method when you submit your form so you can remove this line in edit action and fix your issue
#article.build_images
b. Use a Proc instead of blank_all:
If you look at your code you have
accepts_nested_attributes_for :photos, reject_if: :all_blank, allow_destroy: true
You are using reject_if: :all_blank instead of a Proc, if you look at docs, it says
Passing :all_blank instead of a Proc will create a proc that will reject a record where all the attributes are blank excluding any value for _destroy.
and in your case attributes are still being passed for other image so it's creating a new image for you. You can use a Proc to eliminate this:
accepts_nested_attributes_for :photos, reject_if: proc { |attributes| attributes['image'].blank? }, allow_destroy: true
You are building images for three times always in your article model. When you try to delete image from two images there is also a blank object present for photo that is created by build and when you submit in update action this part #article.update(article_params) add a blank object and also delete an object for photo table. That's why you are getting this issue. Check your params before updating record for photo will solve your issue.
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