“警告:无法批量分配受保护的属性”

Posted

技术标签:

【中文标题】“警告:无法批量分配受保护的属性”【英文标题】:"WARNING: Can't mass-assign protected attributes" 【发布时间】:2011-04-26 01:11:17 【问题描述】:

我使用 RESTful 技术来生成模型(事实上,我正在使用 Devise gem,它为我做这件事),并且我在模型中添加了名为 first_name 和 last_name 的新字段。迁移顺利。我将 attr_accessor :first_name, :last_name 添加到模型中,并希望它能够正常工作。但是当我尝试使用 Doctor.create(:first_name=>"MyName") 等批量分配新实例时,我收到错误消息说我无法批量分配受保护的属性。

我认为使用 attr_accessor 的全部意义在于绕过模型字段的保护。你能帮我理解这条信息吗?

编辑:哦,顺便说一下,记录也没有被创建。我认为它们应该是,因为这只是一个警告,但它们不在数据库中。

Edit2:这是我的模型

class Doctor < User
  has_many :patients
  has_many :prescriptions, :through=> :patients

  validates_presence_of :invitations, :on => :create, :message => "can't be blank"

  attr_accessor :invitations
end

以及没有first_name 和last_name 的模式,因为它们是在users 表中创建的,而users 表是医生的祖先。我使用了单表继承。

create_table :doctors do |t|
  t.integer :invitations

  t.timestamps
end

这是更改用户表的迁移

add_column :users, :first_name, :string
add_column :users, :last_name, :string
add_column :users, :type, :string

编辑:这是种子文件。我不包括 truncate_db_table 方法,但它有效。

%wdoctors patients.each do |m|
  truncate_db_table(m)  
end  

Doctor.create(:invitations=>5, :email=>"email@gmail.com", :first_name=>"Name", :last_name=>"LastName")
Patient.create(:doctor_id=>1, :gender=>"male", :date_of_birth=>"1991-02-24")

【问题讨论】:

我对 Rails 4 还不是很了解,但我认为这个问题是 Rails 3 的问题。 Rails 4 中config/application.rb 中的默认硬编码配置为空白! apidock.com/rails/ActiveRecord/Base/attr_accessible/class 【参考方案1】:

不要将attr_accessorattr_accessible 混淆。访问器内置在 Ruby 中,并定义了一个 getter 方法 - model_instance.foo # returns something - 和一个 setter 方法 - model_instance.foo = 'bar'

Accessible 由 Rails 定义,使属性可批量赋值(与 attr_protected 正好相反)。

如果first_name 是模型数据库表中的一个字段,那么Rails 已经为该属性定义了getter 和setter。您只需添加attr_accessible :first_name

【讨论】:

现在我在搜索种子文件时收到“未知属性”错误。不过,我知道我在数据库中有这个字段;它在迁移文件中... 它在迁移文件中,但是您是否运行了迁移?发布您的种子文件。 你更新其他东西了吗?即使这是错误的,您的模型中仍然有 attr_accessor。 这可能与 STI 对所有子类仅使用一个表这一事实有关,Dave Simms 指出。我有一个医生表,其中有邀请字段,但它应该在用户表中。天哪! 如果您要对 User 表进行子类化,您是否检查了 User 本身的 attr_accessible ?或 attr_protected,但我怀疑是前者。您可以通过在子类中调用 #attr_accessible 来添加可在子类中访问的字段。【参考方案2】:

以完全不适合生产模式的不安全方式破解您的应用程序:

转到 /config/application.rb 向下滚动到您将找到的末尾

config.active_record.whitelist_attributes = true

将其设置为 false。

编辑/顺便说一句(经过 4 个月的 ruby​​ 密集型工作,包括为期 11 周的研讨会): DHH 认为,对于新手(他的话),“正常运行”比“​​非常安全”更重要。

请注意:许多经验丰富的 Rails 开发人员对希望您这样做非常热情。

更新: 3 年后,另一种方法来做到这一点 - 再次,不安全,但比上述解决方案更好,可能是因为您必须为每个模型都这样做

class ModelName < ActiveRecord::Base
  column_names.each do |col|
    attr_accessible col.to_sym
  end
  ...
end

【讨论】:

即使有“早期编码”的警告,这也是一个非常糟糕的建议。开发人员什么时候有机会回去重构他们的整个应用程序?从一开始就做好。 "请记住这是一个安全漏洞,但您可以稍后处理" 您打算什么时候处理它?后来,什么时候生产?我刚刚添加了“您对 attr_accessible 有何看法?”到我的面试问题列表中。 我可以建议进行编辑以澄清您的答案吗?从您的帖子中,尚不清楚默认值是更安全的方式,将其更改为 false 可以轻松访问模型属性,但会打开糟糕的安全漏洞。 如果您要从 rails 3.0x 迁移到 rails 3.2.8,这会有所帮助,暂时禁用它,然后再次启用它 那么,您能解释一下它是如何构成安全漏洞的吗?我的意思是,为了使用 update_atttributes,您需要访问 Rails 应用程序本身的范围。如果有人有,那么该应用程序已经受到威胁。禁用 update_attributes 不会让您的应用程序更安全。【参考方案3】:

不要在这里使用 attr_accessor。 ActiveRecord 在模型上自动创建这些。此外,如果引发验证或批量分配错误,ActiveRecord 将不会创建记录。

编辑:您不需要医生表,您需要一个带有类型列的用户表来处理 Rails Single Table Inheritance。邀请将在用户表上。啊,我在您添加的代码示例中看到您确实有用户类型。摆脱医生表,将邀请转移给用户,我认为你应该没问题。也摆脱 attr_accessor。不需要。

请记住,rails STI 对特定模型的所有类和子类使用同一个表。您的所有医生记录都将是 users 表中类型为“医生”的行

编辑:另外,您确定您只想在创建时验证邀请的存在而不是更新吗?

【讨论】:

但是,当我不使用它时,Rails 会告诉我“未找到方法”,因为我正在搜索种子文件。这种情况我该怎么办? 你能发布你的模型代码和 rake 任务吗?还有创建模型的迁移。确保在数据库中创建了这些列。【参考方案4】:

attr_accessible : variable1, variable2 添加到您的表路由文件中。

【讨论】:

【参考方案5】:

同意@Robert Speicher 的回答但我强烈建议您使用Strong parameter 而不是attr_accessible 来防止大规模分配。

干杯!

【讨论】:

【参考方案6】:

如果您想为单个呼叫(但不是全局)禁用批量分配保护,可以使用:without_protection =&gt; true 选项。我发现这对于迁移和散列的键/值是硬编码或以其他方式已知是安全的其他地方很有用。

此处的示例(也适用于 rails 3.2):https://apidock.com/rails/v3.1.0/ActiveRecord/Base/create/class

【讨论】:

以上是关于“警告:无法批量分配受保护的属性”的主要内容,如果未能解决你的问题,请参考以下文章

警告执行已完成,带有警告存储过程

怎么忽略KEIL的警告

IOS警告消除

Quick.db unwarn 命令取消警告成员中的所有警告

java程序中很多警告,如何修改掉?

将特定级别的所有警告视为错误,而不是下一级的警告