【问题标题】:Is there a more idiomatic way to update an ActiveRecord attribute hash value?是否有更惯用的方法来更新 ActiveRecord 属性哈希值?
【发布时间】:2020-03-11 11:39:05
【问题描述】:

给定一个person ActiveRecord 实例:person.phones #=> {home: '00123', office: '+1-45'}

是否有更符合 Ruby/Rails 习惯的方式来执行以下操作:

person_phones = person.phones
person_phones[:home] = person_phones[:home].sub('001', '+1')
person.update_column :phones, person_phones

示例数据无关紧要。

我只想sub 一个特定的哈希键值和要保存在数据库中的新哈希。我想知道是否有办法做到这一点,只需调用一次person.phones,而不是多次

【问题讨论】:

  • 惯用语是什么意思?你不能person.update_column(:phones, person.phones[:home].sub('001', '+1'))吗?
  • 嗨塞巴斯蒂安!在您的示例中,sub 将返回一个字符串,并在 update_column 时替换整个哈希。所以你不能这样做:(
  • 对了!真的没看到。您正在使用什么 RDBMS 和版本(如果有)?手机是什么数据类型?
  • MYSQL。我知道我不应该将哈希值保存到数据库中,但这是一个已经植入的遗留决定 :)
  • person.phones.class #=> ActiveSupport::HashWithIndifferentAccess,如果对你有帮助的话。

标签: ruby-on-rails ruby


【解决方案1】:

在不改变太多行为的情况下:

person.phones[:home].sub!('001', '+1')
person.save

这里有一些重要的区别:

  1. 您可以使用sub! 而不是sub 来修改字符串对象。这意味着所有其他持有对该字符串的引用的变量/对象也将发生变化。
  2. 我使用的是save 而不是update_column。这意味着不会跳过回调,所有更改都会保存,而不仅仅是 phones 属性。

根据我的评论,您正在寻找一个与上述没有什么不同的单班轮:

person.tap { |person| person.phones[:home].sub!('001', '+1') }.save

【讨论】:

  • 谢谢!正是我想要的
【解决方案2】:

您可以在模型上使用before_validation 回调。

像这样:

class Phone < ApplicationRecord

 validates :home, US_PHONE_REGEX

 before_validation :normalize_number

private

 def normalize_number
  home.gsub!(/^001/, '+1')
 end
end

注意:我没有测试过这段代码,它只是为了展示一种方法。

如果您还希望标准化一个国际号码,请评估使用像 phony 这样的库是否更有意义,或者基于它的 rails lib https://github.com/joost/phony_rails

编辑 因为评论澄清你只想改变哈希值,就像你可以使用Ruby的方法transform_values!

phones.transform_values!{|v| v.gsub(/^001/, '+1')}

【讨论】:

  • 嗨,保罗!示例数据无关紧要。我只想知道这是否可以在一行中完成,而不必多次调用person.phones
  • 谢谢保罗!只有两个小 cmets:1)我只想 sub 一个特定的哈希键,而不是全部。 2) 我希望将更改保存在数据库中。我想知道是否有办法做到这一点,只需调用一次person.phones,而不是多次。再次感谢!
猜你喜欢
  • 2015-09-10
  • 2013-11-07
  • 2019-03-22
  • 2021-01-22
  • 2011-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-19
相关资源
最近更新 更多