为什么不能使用符号而不是字符串来访问 Rails 模型属性?
我需要比较数据库更新前后的一些 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
当您在 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 ofHash
calledHashWithIndifferentAccess
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.
您可以用自己的方法覆盖属性方法。
打开您的 WorkEffort 类,
然后当您调用 work_effort.attributes 时,您将在哈希中拥有符号化的键。
You can overwrite the attributes method with your own.
Open your WorkEffort class
then when you call work_effort.attributes, you will have symbolized keys in the hash.
您可能想使用:stringify_keys!它在 Rails 代码中广泛使用。
You may want to use : stringify_keys! which is extensively used all over Rails code.
所有 Rails 模型都可以具有符号化属性名称:
然后您可以调用
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:
Then you can call
MyModel.attribute_names(symbols = true)
and get back[:id, :my_attribute, :updated_at, :created_at, ...]
while still allowingattribute_names
to return strings.Note that you need the parentheses after
super
to avoid an argument error with the originally defined function.