Reputation: 822
I've a Rails model called Biography, and biography has one lifestyle.
class Lifestyle < ActiveRecord::Base
belongs_to :biography
end
class Biography < ActiveRecord::Base
has_one :lifestyle, dependent: :destroy
accepts_nested_attributes_for :lifestyle
end
And In my BiographyController I've this:
def update_biography
biography = current_user.biography
logger.debug("params are: #{params}")
logger.debug("biography_params are: #{biography_params}")
if biography.update(biography_params)
render :json => biography
else
render :json => { :errors => biography.errors.full_messages }, :status => 400
end
end
def biography_params
params.require(:biography).permit(
:disability, :hiv_positive, :blood_type,
lifestyle_attributes: [:id, :diet, :smoke, :drink])
end
And this is what I get from my two logger.debug statements above:
params are: {"lifestyle_attributes"=>{"diet"=>"2", "smoke"=>"false", "drink"=>"2"}, "disability"=>"false", "hiv_positive"=>"false", "blood_type"=>"3", "controller"=>"biographies", "action"=>"update_biography", "id"=>"4", "biography"=>{"disability"=>"false", "hiv_positive"=>"false", "blood_type"=>"3"}}
biography_params are: {"disability"=>"false", "hiv_positive"=>"false", "blood_type"=>"3"}
Why is that my biography_params do not contain lifestyle_attributes even though I've accepts_nested_attributes_for statment in the Biography model, and also defining association between Biography and Lifestyle in the models? I've also added lifestyle_attributes in the strong parameters permit list.
However, if I run this in rails console the assignment does work:
b = Biography.first
b.update("lifestyle_attributes"=>{"diet"=>"2", "smoke"=>"false", "drink"=>"2"})
Upvotes: 2
Views: 1706
Reputation: 76784
The problem is that lifestyle_attributes
are not a part of the biography
params hash. You should have:
params: {
biography: {
lifestyle_attributes: {
...
}
}
}
This will allow the params method to access the data properly.
To explain how it works, you need to look at how the ActionController::Parameters
class works:
Returns a new
ActionController::Parameters
instance that includes only the given filters and sets the permitted attribute for the object to true.
Each time you use params.require(:x).permit(:y)
, it will return a new hash with only the permitted params. These permitted params have to be nested within the required param.
As you've demonstrated, this works well...
biography_params are:
{"disability"=>"false", "hiv_positive"=>"false", "blood_type"=>"3"}
The problem is that because lifestyle_attributes
is not nested under biography
, its parameters are not returned after you call the params method.
The fix for this will be in your form:
#app/views/biographies/new.html.erb
<%= form_for @biography do |f| %>
<%= ... biography attributes %>
<%= f.fields_for :lifestyle do |l| %>
<%= lifestyle fields %>
<% end %>
<%= f.submit %>
<% end %>
I don't know how you've done it currently, but somehow, you've attached lifestyle
attributes outside of the biography
hash.
Upvotes: 1
Reputation: 1779
require
and permit
are actually the method of ActionController::Parameters
. The require
which in this case is the :biography
needs to be present in the hash you are sending from your backbone views.
The require
method ensures that a specific parameter is present, and if it's not provided, the require method throws an error. It returns an instance of ActionController::Parameters
for the key passed into require i.e :biography
.
Upvotes: 2
Reputation: 12340
You can try
params = {biography: {first_name: "new", last_name: "user", disability: false, hiv_positive: false, blood_type: 3, "lifestyle_attributes: {diet: "2", smoke: "false", drink: "2"}}
If you do not want biography:
on your params you can ignore require(:biography)
on params.require(:biography)
to just params.permit(...)
Hope now it will work
You get more info on Nested Attributes
Upvotes: 1