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

发布于 2024-12-09 15:42:35 字数 1032 浏览 0 评论 0原文

我需要比较数据库更新前后的一些 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 来获取模型属性。有其他人经历过这个,解决过这个问题,也被这个困惑过吗?提前致谢

I need to compare some Rails (2.3.11) model attribute values before and after a database update, so I start by finding my record and saving the existing attribute values in a hash, as follows:

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

Note I am adhering to the "best practice" of using a symbol for a hash key.

Then I have a method that takes the model and the hash to determine possible additional steps to take if the values from the hash and the model attributes don't match. To determine this I tried to get at the model attribute value in an each loop but I was getting nil at first:

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

In debugging the above I noticed that hard-coding a string as a key:

work_effort.attribute['scheduled_completion_date']

Returned the desired value. So then in my each block I tried the following and it worked:

model_val = work_effort.attributes[key.to_s]

Is there a different way to do this? To me, with just 3 months Ruby/Rails experience, it's confusing to use symbols as hash keys as is the prescribed best practice, but then have to call .to_s on the symbol to get at a model attribute. Has anybody else experienced this, worked around this, been confused by this too? Thanks in advance

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

淡淡離愁欲言轉身 2024-12-16 15:42:36

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

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

尝试使用 work_effort.attributes.with_in Different_access[key]

实际上,它只是在做与您相同的事情,但它是在幕后完成的。

The Hash returned when you call #attributes on a AR instance has string keys, which is why a symbol as an index into the hash doesn't work in your case. There is a subclass of Hash called HashWithIndifferentAccess which automatically converts symbol indexes into strings.

Quite often in Rails you'll encounter HashWithIndifferentAccess instances. A perfect example is the params variable you access in your controller and view code.

Try using work_effort.attributes.with_indifferent_access[key]

Really it is just doing the same thing that you are, but it does it behind the scenes.

高冷爸爸 2024-12-16 15:42:36

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

打开您的 WorkEffort 类,

class WorkEffort
  def attributes
    super.symbolize_keys
  end
end

然后当您调用 work_effort.attributes 时,您将在哈希中拥有符号化的键。

You can overwrite the attributes method with your own.

Open your WorkEffort class

class WorkEffort
  def attributes
    super.symbolize_keys
  end
end

then when you call work_effort.attributes, you will have symbolized keys in the hash.

我乃一代侩神 2024-12-16 15:42:36

您可能想使用: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

You may want to use : stringify_keys! which is extensively used all over Rails code.

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
后来的我们 2024-12-16 15:42:36

所有 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 返回字符串。

请注意,您需要在后面加上括号super 以避免最初定义的函数出现参数错误。

All of your Rails models can have symbolized attribute names:

class ApplicationRecord < ActiveRecord::Base
  # ...

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

      super()
    end
  end
end

Then you can call MyModel.attribute_names(symbols = true) and get back [:id, :my_attribute, :updated_at, :created_at, ...] while still allowing attribute_names to return strings.

Note that you need the parentheses after super to avoid an argument error with the originally defined function.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文