我有一个Car模型和一个品牌模型(每个模型都有_and_belongs_to_many)。我正在努力为汽车创造一个新的品牌。汽车模型接受品牌的嵌套属性。
当我尝试
时@car = Car.find(params[:car_id])
@brand = @car.brands.build(params[:brand])
我收到了一个质量分配错误:
Can't mass-assign protected attributes: brands
品牌控制器:
class BrandsController < ApplicationController
def create
debugger
@car = Car.find(params[:car_id])
@brand= @car.brands.build(params[:brand])
if @brand.save
redirect_to @car
else
#do something
end
end
end
使用
调用调试器pp params
节目:
"brand"=>{"brands"=>{"name"=>"BMW"}},
"commit"=>"Create Brand",
"action"=>"create",
"controller"=>"brands",
"car_id"=>"4"}
视图如下所示:
<%= form_for([@car, @car.brands.build]) do |f| %>
<div class="field">
<%= f.fields_for :brands do |a| %>
<%= a.text_field :name, placeholder: "New Brand" %>
<% end %>
</div>
<p><%= f.submit %></p>
<% end %>
我希望应用程序是安全的,所以我想单独设置品牌属性,而不是传递整个“品牌”哈希(顺便说一下,它不起作用)。我怎样才能获得品牌名称?像
这样的东西params[:brands].name?
修改
我可以用'params [:brand] [:brands] [:name]'获得品牌名称。现在事实证明,品牌表没有car_id,因为它连接到带有连接表的汽车。
答案 0 :(得分:1)
<%= form_for @car do |f| %>
<div class="field">
<%= f.fields_for :brands do |a| %>
<%= a.text_field :name, placeholder: "New Brand" %>
<% end %>
</div>
<p><%= f.submit %></p>
<% end %>
在你的模特中:
class Car < ActiveRecord::Base
has_many :brands
accepts_nested_attributes_for :brands
attr_accessible :brand_attributes
end
答案 1 :(得分:1)
虽然在某些情况下让模型接受另一个模型是一个有用的工具,但它往往很麻烦,并且不仅在模型级别而且在控制器和视图层都违反了单一责任主体。如果您的设计需要改变,最终会使重构变得更加困难。
遵循REST的理念,而不是通过汽车创建品牌,使用BrandsController来处理品牌的生命周期。
至于您对安全性的要求,手动设置模型的属性并不提供任何更多的安全性,因为假设您正确使用attr_accessible,则传递整个params哈希。给它一个可接受的参数的白名单会告诉AR界面其他一切都应该被拒绝。
class Brand < ActiveRecord::Base
attr_accessible :name
end
因此,如果一个品牌属于汽车,并且有人试图通过将car_id黑客入侵到表单来改变关系,那么它会拒绝来自哈希的car_id,甚至可能根据您的设置提出异常。
config.active_record.mass_assignment_sanitizer = :strict
这将导致ActiveModel::MassAssignmentSecurity::Error
而不是仅输出到日志中的警告。