Rails でシリアル化されたフィールドを更新する

概要

Rails モデルにシリアル化された属性があり、それを更新しようとしています。 Block#preferences 属性は、最初は移行によってテキスト タイプとして生成されました。 Postgresql14を使用しています。

# == 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

強力なパラメーターの要点は、任意のパラメーターを許可しないことだと理解しています。しかし、質問したいのですが、許可されていないパラメーターをログに記録せずに、シリアル化された属性を通じて動的キーを更新できるようにするにはどうすればよいですか。

以下は、更新アクションに渡されるパラメーターです。

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"
}

これらは設定として保存されます: {“title_text”=>“Rent with us”、“description_text”=>“Send us a message.”、“hero_image”=>“placeholder/block01.jpg”、“bg_color”=>” Block オブジェクトの #000000”}。これらの設定属性は単なる例であり、logo_text や links_alignment などの他の属性もあります。このリストは増え続けています。

更新アクションは、更新する属性として設定属性を指定した場合に機能します。

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

ただし、block_params を指定して update を呼び出すと、フィールドが許可されていないことがログに表示されます。

許可されていないパラメータ: :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

これは私たちの環境では少し斬新なように見えるので、serialize が params[:block] から設定をマッピングできるが、block_params を使用して update を呼び出すことができない方法を学習しようとしています。ご指導をよろしくお願いいたします。

解決策

パラメーターのホワイトリスト登録は、スキーマやモデルが一般的にデータに対して行っている処理とはほとんど関係がありません。

ActionContoller::Parameters は単なるハッシュのようなオブジェクトであり、これを ActiveRecord 永続化メソッド (new、create、update など) に渡すと、渡されたパラメーター インスタンスに許可フラグが設定されていない場合に発生します。これは、ActionContoller::Parameters のネストされたインスタンスにも当てはまります。

さらに、ActionContoller::Parameters には action_on_unpermitted_parameters 設定オプションがあり、permit を呼び出したときにホワイトリストにないキーが存在する場合に、ログに記録したり、発生させたり、何も行わなかったりします。

一括割り当て保護を完全に無効にするには、permit! を使用できます。

def block_params
  params.require(:block).permit!
end

これにより、ActionContoller::Parameters のこのインスタンスのすべてのキーとネストされたパラメーターが許可されます。

任意のキーを持つハッシュを処理する核の少ないオプションが必要な場合は、受信パラメータのキーのリストを渡すだけです。

def block_params
  params.require(:block).permit(*params[:block].keys.map(&:to_sym))
end

ここでの違いは、許可されるスカラー値のみが許可され、配列やハッシュは許可されないことです。