为啥不能使用符号而不是字符串来访问 Rails 模型属性?

Posted

技术标签:

【中文标题】为啥不能使用符号而不是字符串来访问 Rails 模型属性?【英文标题】:Why aren't Rails model attributes accessible using symbols instead of strings?为什么不能使用符号而不是字符串来访问 Rails 模型属性? 【发布时间】:2011-12-06 09:39:01 【问题描述】:

我需要在数据库更新前后比较一些 Rails (2.3.11) 模型属性值,所以我首先查找我的记录并将现有属性值保存在哈希中,如下所示:

id = params[:id]
work_effort = WorkEffort.find(id)

ancestor_rollup_fields = 
    :scheduled_completion_date => work_effort.scheduled_completion_date


work_effort.update_attributes(params.except(:controller, :action))
#etcetera

请注意,我坚持使用符号作为哈希键的“最佳实践”。

然后我有一个方法,它采用模型和散列来确定如果散列和模型属性中的值不匹配可能采取的额外步骤。为了确定这一点,我尝试在每个循环中获取模型属性值,但一开始我得到了 nil:

def rollup_ancestor_updates(work_effort, ancestor_rollup_fields)
    ancestor_rollup_fields.each do |key, value|
        model_val = work_effort.attributes[key] #nil
        #etcetera

在调试上述内容时,我注意到将字符串硬编码为键:

work_effort.attribute['scheduled_completion_date']

返回所需的值。因此,在我的每个块中,我尝试了以下操作,并且成功了:

model_val = work_effort.attributes[key.to_s]

有其他方法可以做到这一点吗?对我来说,只有 3 个月的 Ruby/Rails 经验,按照规定的最佳实践将符号用作哈希键是令人困惑的,但随后必须在符号上调用 .to_s 才能获得模型属性。有没有其他人经历过这个,解决过这个问题,也对此感到困惑?提前致谢

【问题讨论】:

【参考方案1】:

当您在 AR 实例上调用 #attributes 时返回的哈希具有字符串键,这就是为什么符号作为哈希索引在您的情况下不起作用的原因。 Hash 的一个子类称为HashWithIndifferentAccess,它会自动将符号索引转换为字符串。

在 Rails 中,您经常会遇到 HashWithIndifferentAccess 实例。一个完美的例子是您在控制器和查看代码中访问的 params 变量。

尝试使用work_effort.attributes.with_indifferent_access[key]

确实,它只是在做和你一样的事情,但它是在幕后做的。

【讨论】:

它有效,但有什么理由我不应该使用 key.to_s 或者是大多数人都使用 with_indifferent_access 的令人信服的论点? 除非我有非常令人信服的理由,否则我只会使用 key.to_s。在您的情况下,似乎 key.to_s 实际上比 with_indifferent_access 便宜【参考方案2】:

您可以用自己的方法覆盖属性方法。

打开你的 WorkEffort 课程

class WorkEffort
  def attributes
    super.symbolize_keys
  end
end

那么当你调用 work_effort.attributes 时,你将在哈希中拥有符号化的键。

【讨论】:

【参考方案3】:

您可能想使用:stringify_keys!在 Rails 代码中广泛使用。

def rollup_ancestor_updates(work_effort, ancestor_rollup_fields)
  ancestor_rollup_fields.stringify_keys!.each do |key, value|
    model_val = work_effort.attributes[key]
  end
  #....
end

【讨论】:

如何将其放入我的代码示例中以使其在不使用 key.to_s 的情况下工作?谢谢;与此同时,我正在检查另一个示例(with_indifferent_access,现在我记得在其他地方看到过)。 我想你应该说symbolize_keys!。这个想法比调用with_indifferent_access 更好,因为它不会创建新的哈希。当然,除非您需要同时使用字符串和符号键。【参考方案4】:

所有 Rails 模型都可以有符号化的属性名称:

class ApplicationRecord < ActiveRecord::Base
  # ...

  class << self
    def attribute_names(symbols = false)
      return self.attribute_names.map(&:to_sym) if symbols

      super()
    end
  end
end

然后您可以调用MyModel.attribute_names(symbols = true) 并返回[:id, :my_attribute, :updated_at, :created_at, ...],同时仍然允许attribute_names 返回字符串。

请注意you need the parentheses after super 以避免最初定义的函数出现参数错误。

【讨论】:

以上是关于为啥不能使用符号而不是字符串来访问 Rails 模型属性?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Heroku 使用服务器时间而不是 Rails 时区进行日志记录?

Ruby/Rails - 为啥自我关联条件取决于创建时间?

为啥 Rails 5 使用 ApplicationRecord 而不是 ActiveRecord::Base?

由于符号而不是 Rails 导致哈希失败的 RSpec 测试

为啥符号不是冻结的字符串?

为啥文件名不能出现特殊符号?