5

有没有办法自动对使用 ActiveRecord::Base.store 存储的值进行类型转换?

举这个完全不切实际的例子:

class User < ActiveRecord::Base
  store :settings, accessors: [ :age ]
end

user = User.new(age: '10')
user.age # => '10'

我知道我可以重写年龄的阅读器方法以将其转换为整数,但我很好奇是否有一种未记录的方法。

试图避免这种情况:

class User < ActiveRecord::Base
  store :settings, accessors: [ :age ]

  def age
    settings[:age].to_i
  end
end

user = User.new(age: '10')
user.age # => 10

更新

寻找类似的东西:

class User < ActiveRecord::Base
  store :settings, accessors: {:age => :to_i}
end

或者:

class User < ActiveRecord::Base
  store :settings, accessors: {:age => Integer}
end
4

6 回答 6

2

从 Rails 3.2.7 开始,没有自动类型转换值的方法。如果我遇到一种方法,我会更新这个问题:/

于 2012-07-27T12:13:21.497 回答
1

我知道有两种方法可以做到这一点。其中之一是您每次设置时都会转换它。另一个只有在将其保存到数据库时才进行转换。

选项一:自定义设置器

class User < ActiveRecord::Base

  ...

  # public method
  def age=(age)
    self.settings[:age] = age.to_i
  end

  ...

end

在控制台中:

$ user.age = '12'     # => "12"
$ user.age            # => 12
$ user.age.class      # => Fixnum
$ user = User.new age: '5'
$ user.age.class      # => Fixnum

选项二: before_save 调用(或不同的调用前)

class User < ActiveRecord::Base
  before_save :age_to_int

  ...

  private

    def age_to_int
      # uncomment the if statement to avoid age being set to 0
      # if you create a user without an age
      self.age = self.age.to_i # if self.age 
    end

end

在控制台中

$ user = User.new(age: '10')
$ user.save
$ user.age            # => 10
$ user.age.class      # => Fixnum

方案二的缺点:

$ user.age = '12'
$ user.age            # => "12"

如果我是你,我会使用自定义设置器。如果您想要一个独立于数据库列(它是一个字符串)的默认值,请在设置器之外使用 before_save。

于 2012-07-20T18:17:09.830 回答
1

StorextActiveRecord::Store.store_accessor.

于 2016-08-30T00:29:03.973 回答
0

最好链接存储访问器方法而不是覆盖它们,因为这些魔法咒语创建的方法从来没有你想象的那么简单:

define_method(key) do
  send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash)
  send(store_attribute)[key]
end

因此,例如在整数的情况下,我会这样做:

def age_with_typecasting
  ActiveRecord::ConnectionAdapters::Mysql2Adapter::Column.value_to_integer(age_without_typecasting)
end

alias_method_chain :age, :typecasting

同样,它不是内置的,但可以解决问题。它还利用您的数据库连接适配器将存储在数据库中的字符串转换为您想要的值类型。根据您使用的数据库更改适配器。

于 2015-07-14T09:59:54.027 回答
0

我们没有找到任何未记录的方法,但即使您找到了,也不建议您将代码依赖于 Rails 私有方法。

这是我们在 Rails 5.2 中使用支持的 API 的方式。假设您有一个费率模型并希望将每日费率存储在名为 :rate_per_day 的单个数据库列中(我们使用的是 MySQL,因此列的类型是 TEXT):

class Rate < ApplicationRecord

  DAILY_RATES = [
    :monday_rate,
    :tuesday_rate,
    :wednesday_rate,
    :thursday_rate,
    :friday_rate,
    :saturday_rate,
    :sunday_rate,
  ]

  store :rate_per_day, accessors: DAILY_RATES, coder: JSON

  DAILY_RATES.each do |method|

    define_method(method) do
      super().try(:to_i)
    end

    define_method("#{method}=") do |value|
      super(value.try(:to_i))
    end

  end

这是官方支持的。阅读https://api.rubyonrails.org/classes/ActiveRecord/Store.html中的覆盖默认访问器。

顺便说一句,我们使用新的 Attributes API 进行了调查,因为它支持自动类型转换,但是除非我们创建自定义类型对象,否则无法找到将所有数据存储在单个列中的方法。

于 2018-10-05T14:06:35.560 回答
0

activerecord-typedstore 对此非常有效,并允许您防止 null 和空白值:

https://github.com/byroot/activerecord-typedstore

于 2018-12-23T19:52:42.490 回答