Reputation: 651
Updating Nested Attributes append instead of updating in has many relationships I am trying to use Rails 4 Update_attributes
Class Person <ActiveRecord::Base
has_many :pets
accepts_nested_attributes_for :pets
end
Class Pet < ActiveRecord::Base
belongs_to :person
end
In my controller I am recieveing the params as {id: 23, house_no:'22A', pets: [{name:'jeffy', type:'dog'}, {name:'sharky', type:'fish'}]}
and my update method is
def update
@Person = Person.find(params[:id])
if @Person.update(person_params)
@Person.save
render 'persons/create', status 200
else
render 'persons/create', status 400
end
end
private
def person_params
person_params = params.permit(:house_no)
person_params.merge! ({pets_attributes: params[:pets]}) if params[:pets].present?
person_params
end
Now when I update it and if the person already has a pet then the new pets gets appended instead of getting updated eg if a person with id 1 has 1 pet named "Tiger" and I update that person with 2 pets named "Shasha" and "Monti" then the person record has 3 pets (Tiger, Shasha and Monti) instead of updating it to 2 (Shasha and Monti)
Upvotes: 29
Views: 44274
Reputation: 530
You have to add the :id
attribute to the :pets_attributes
array in person_params
.
You need to permit the id attribute to update the records.
Upvotes: 27
Reputation: 325
You need to edit you params with pets_attributes
Also need to add :id paramter in the pet objects.
Upvotes: 2
Reputation: 394
see the manual:
http://api.rubyonrails.org/v4.0.1/classes/ActiveRecord/NestedAttributes/ClassMethods.html
you need to send in your attributes like pets_attributes:[{name:'jeffy', type:'dog'}, {name:'sharky', type:'fish'}]
and it should work fine.
Please read
You can now set or update attributes on the associated posts through an attribute hash for a member: include the key :posts_attributes with an array of hashes of post attributes as a value.For each hash that does not have an id key a new record will be instantiated, unless the hash also contains a _destroy key that evaluates to true.
Upvotes: 19
Reputation: 33552
Try changing your person_params
as
def person_params
person_params = params.require(:person).permit(:house_no ,pets_attributes: [:name,:type])
end
And remove @person.save
from the update method.You don't need it.And also use !
for update
def update
@Person = Person.find(params[:id])
if @Person.update!(person_params)
render 'persons/create', status 200
else
render 'persons/create', status 400
end
end
Update
And also,as i can see there is no belongs_to :person
association in Pet
model,Add it in the model
Class Pet < ActiveRecord::Base
belongs_to :person
end
And now permit the person_id
attribute by adding it in the params
def person_params
person_params = params.require(:person).permit(:house_no,pets_attributes: [:name,:type,:person_id])
end
Upvotes: 1
Reputation: 53048
Update the Person
model as below:
class Person < ActiveRecord::Base ## NOTE: class (c lowercase) and NOT Class
has_many :pets
accepts_nested_attributes_for :pets, allow_destroy: true ## Added allow_destroy
end
In order to destroy the associated model i.e., Pet
through the attributes hash, you have to enable it first using the :allow_destroy
option.
Then, from your view, you will need to pass the _destroy
attribute for the pets that you would like to be removed.
Since, you haven't shared the view specific code. Following is an example of how it should be implemented in your view. Change it according to your requirement:
<%= form_for @person do |f| %>
<%## .. %>
<%= f.fields_for :pets do |builder| %>
<%## .. %>
<%= builder.check_box :_destroy %>
<%= builder.label :_destroy, "Remove Pet" %>
<% end %>
<% end %>
In the above code, you would need to add the checkbox
and label
for passing _destroy
value.
After this, update the person_params
method in your controller as below:
def person_params
params.require(:person).permit(:house_no, pets_attributes: [:id, :name, :type, :person_id, :_destroy])
end
NOTE:
You should not be defining instance variables
with capitalized name. Capitalized names are used for declaring Constants
in Ruby
.
def update
@person = Person.find(params[:id])
if @person.update(person_params)
@person.save
render 'persons/create', status 200
else
render 'persons/create', status 400
end
end
Upvotes: 16
Reputation: 22956
Probably 1. you would want to assign pets separately,
@Person.pets = Pets.find(params[:pets])
Or 2. clear the pets before saving Person.
@Person.pets.destroy
Upvotes: 1