Rails 8 Strong Parameters:双括号修复用于嵌套属性
Source: Dev.to
请提供您希望翻译的具体文本内容,我将为您翻译成简体中文并保留原有的 Markdown 格式。
Problem
在升级到 Rails 8 时,你可能会开始使用 params.expect——通常是 RuboCop 的 Rails/StrongParametersExpect 提示的——以使 strong‑parameter 合约更加明确。
当嵌套属性以索引哈希的形式提交时,会出现一个棘手的问题:它们可能会被静默过滤,导致验证以不明显的方式失败。
Model
class Invoice {
"date" => "2026-01-11",
"due_date" => "2026-01-12",
"client_id" => "123",
"line_items_attributes" => {
"0" => {
"description" => "Web Development",
"quantity" => "10",
"unit_price" => "150"
},
"1" => {
"description" => "UI Design",
"quantity" => "5",
"unit_price" => "200"
}
}
}
⚠️ Important:
line_items_attributes不是数组;它是一个以动态数字字符串("0"、"1"、…)为键的哈希。这一区别是 bug 的根本原因。
使用 require/permit(有效)
def invoice_params
params.require(:invoice).permit(
:date,
:due_date,
:client_id,
line_items_attributes: [:description, :quantity, :unit_price, :_destroy]
)
end
使用 permit 时,嵌套属性被正确分配,验证通过。
使用 expect(失败)
def invoice_params
params.expect(
invoice: [
:date,
:due_date,
:client_id,
line_items_attributes: [:description, :quantity, :unit_price, :_destroy]
]
)
end
- 未抛出强参数错误。
line_items_attributes被过滤掉,因此没有分配任何行项目。validates :line_items, presence: true失败,发票未被持久化。
检查结果:
invoice_params[:line_items_attributes]
# => #
声明 line_items_attributes: [:description, :quantity, :unit_price] 告诉 Rails 期望 一个包含这些键的单层嵌套哈希。然而,传入的数据是 一个带索引的哈希集合。
修复方法:集合使用双括号
要表达“嵌套哈希的集合”,必须将内部数组再包裹在另一个数组中:
def invoice_params
params.expect(
invoice: [
:date,
:due_date,
:client_id,
line_items_attributes: [[
:id,
:description,
:quantity,
:unit_price,
:_destroy
]]
]
)
end
- 内部数组 → 每个行项目允许的键。
- 外部数组 → 表示重复/类似集合的结构。
这同时匹配:
- 索引哈希(
"0" => {...}) - 哈希数组(
[{...}, {...}])
修复前后测试
# Before fix – no line items created
expect {
post invoices_path, params: valid_params
}.to not_change(Invoice, :count)
.and change(LineItem, :count).by(0)
# After fix – line items created
expect {
post invoices_path, params: valid_params
}.to change(Invoice, :count).by(1)
.and change(LineItem, :count).by(2)
形状 vs. expect 声明 摘要
| 形状 | expect 声明 |
|---|---|
| 单层嵌套哈希 | line_items_attributes: [:description] |
索引哈希 ("0"=>…) | line_items_attributes: [[:description]] |
| 哈希数组 | line_items_attributes: [[:description]] |
如果嵌套记录消失或验证意外失败,请检查传入参数的 形状,而不仅仅是其值。
Source: …
Rails 8 迁移指南
- RuboCop 常常建议将
params.require(...).permit(...)替换为params.expect(...)。 - Rails/StrongParametersExpect cop 并不会自动处理 Rails 嵌套表单使用的索引哈希格式(
*_attributes)。草率的重写可能会改变行为。 - 对于
has_many嵌套属性,始终使用双括号语法([[...]])。 - 当参数高度动态或结构复杂时,保留
require/permit或在局部禁用该 cop 可能更为合理。
要点
params.expect比permit更严格,必须明确指定结构。- 对于嵌套哈希的集合使用
[[...]]。 - 验证失败可能是静默丢弃嵌套属性的唯一表现。
- 在更改强参数处理方式时,请求规范(request specs)至关重要。
如果这帮你省了时间——或者在你发现之前让你付出了代价——你并不孤单。