I have a serialized attribute in my Rails model and am attempting to update it. The Block#preferences
attribute was first generated through a migration as a text type. We use Postgresql 14.
# == Schema Information
#
# Table name: blocks
#
# id :bigint not null, primary key
# name :string
# preferences :text
# created_at :datetime not null
# updated_at :datetime not null
# page_id :bigint not null
#
class Block < ApplicationRecord
belongs_to :page
serialize :preferences, JSON
end
I understand that the whole point of strong parameters is not allowing arbitrary parameters. But to ask a question, how do you allow dynamic keys to be updated through a serialized attribute without logging Unpermitted parameters
.
Below is the parameters being passed to the update
action:
Parameters: {
"authenticity_token"=>"[FILTERED]",
"block"=>{
"title_text"=>"Hello world!",
"description_text"=>"Send us a message.",
"hero_image"=>"placeholder/block01.jpg",
"bg_color"=>"#652020"
},
"commit"=>"Update Block",
"id"=>"23"
}
These are saved as preferences: {"title_text"=>"Rent with us", "description_text"=>"Send us a message.", "hero_image"=>"placeholder/block01.jpg", "bg_color"=>"#000000"}
on the Block
object. These preferences attributes are just an example - there are other attributes such as logo_text
and links_alignment
as well. This list is ever growing.
The update
action works if you specify the preferences
attribute as the attribute to be updated.
def update
@block = Block.find(params[:id])
respond_to do |format|
if @block.update(preferences: params[:block])
end
end
end
private
def block_params
params.require(:block).permit(
:name,
:preferences
)
end
However, if I call update
with the block_params
the log shows that the field is unpermitted:
Unpermitted parameters: :title_text, :description_text, :hero_image, :bg_color.
def update
@block = Block.find(params[:id])
respond_to do |format|
if @block.update(block_params)
end
end
end
private
def block_params
params.require(:block).permit(
:name,
:preferences
)
end
As this is seem a bit novel in our environment, I'm looking to learn how serialize is able to map the preferences from the params[:block]
but cannot call update
using the block_params
. Thanks a lot in advance for any guidance!
Parameter whitelisting has almost nothing to do with your schema or what the model is doing with the data in general.
ActionContoller::Parameters
is simply a hash like object and when you pass it to the ActiveRecord persistence methods (new, create, update, etc) it will raise if the passed parameters instances do not have their permitted
flag set. This also applies to nested instances of ActionContoller::Parameters
.
Additionally ActionContoller::Parameters
has a action_on_unpermitted_parameters
configuration option which will log, raise or do nothing when you call permit
and there are keys present which are not in the whitelist.
To disable the mass assignment protection completely you can use permit!
:
def block_params
params.require(:block).permit!
end
This permits all keys of this instance of ActionContoller::Parameters
and any nested parameters.
If you want a less nuclear option to handle hashes with arbitrary keys you can just pass the list of keys of the incoming parameters:
def block_params
params.require(:block).permit(*params[:block].keys.map(&:to_sym))
end
The difference here is that this will only permit permitted scalar values and not arrays and hashes.