less than 1 minute read

問題描述

升級到 Rails 8 後,將 params.require 改為 params.except 的用法後,遇到了 accepts_nested_attributes_for 無法正常運作的問題。

Rails 8 的參數處理變更

Rails 8 需要將原本的 params.require 改成 params.except

# Rails 7 的寫法
user_params = params.require(:user).permit(:name, :handle)

# Rails 8 的寫法
user_params = params.except(user: [:name, :handle])

問題現象

然而修改後,accepts_nested_attributes_for 在建立相關 record 時卻出現無法建立的狀況。

# User model
class User < ApplicationRecord
  has_many :roles
  accepts_nested_attributes_for :roles, allow_destroy: true
end

# Controller
def user_params
  params.except(
    user: [:name, :handle,
           roles_attributes: [:id, :name, :_destroy]]
  )
end

看起來語法沒有錯誤,但 log 中卻沒有出現 roles 相關的 SQL 語句。

解決方案

後來發現,Rails 8 在處理 has_many 關聯時,需要額外加上雙括號來表示這是一個陣列屬性:

def user_params
  params.except(
    user: [:name, :handle,
           roles_attributes: [[:id, :name, :_destroy]]]
  )
end

關鍵差異

  • Rails 7: roles_attributes: [:id, :name, :_destroy]
  • Rails 8: roles_attributes: [[:id, :name, :_destroy]]

注意 roles_attributes 的值需要用雙括號 [[]] 來包裝,這樣 Rails 8 才能正確識別這是一個陣列屬性。

總結

Rails 8 的 params.except 語法與 accepts_nested_attributes_for 配合使用時,需要特別注意 has_many 關聯的參數格式。使用雙括號 [[]] 來表示陣列屬性,這樣才能確保巢狀屬性能夠正常運作。

參考文章