16

我有一个带有一些属性和一个虚拟属性的模型。此虚拟属性用于在创建表单中制作复选框。

class Thing < ActiveRecord::Base
  attr_accessor :foo
  attr_accessible :foo
end

由于该字段是表单中的复选框,因此该foo属性将接收'0''1'作为值。由于以下代码,我希望它是一个布尔值:

class Thing < ActiveRecord::Base
  attr_accessor :foo
  attr_accessible :foo

  before_validation :set_default_bar

  private

  def set_default_bar
    self.bar = 'Hello' if foo
  end
end

这里的问题是即使在foois时条件也会为真'0'。我想使用 ActiveRecord 类型转换机制,但我发现这样做的唯一方法如下:

class Thing < ActiveRecord::Base
  attr_reader :foo
  attr_accessible :foo

  before_validation :set_default_bar

  def foo=(value)
    @foo = ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value)
  end


  private

  def set_default_bar
    self.bar = 'Hello' if foo
  end
end

但我觉得那样做很脏。有没有更好的选择而不重写转换方法?

谢谢

4

4 回答 4

15

您在原始帖子中的解决方案对我来说似乎是最好的解决方案。

class Thing < ActiveRecord::Base
  attr_reader :foo
  def foo=(value)
    @foo = ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value)
  end
end

如果你想稍微清理一下,你总是可以创建一个帮助方法来foo=为你定义你的 writer 方法,使用value_to_boolean.

我可能会创建一个带有调用方法的模块,bool_attr_accessor以便您可以将模型简化为如下所示:

class Thing < ActiveRecord::Base
  bool_attr_accessor :foo
end

似乎 ActiveModel 应该为我们提供类似的东西,以便虚拟属性更像“真实”(ActiveRecord-persisted)属性。每当您有一个从表单提交的布尔虚拟属性时,这种类型转换是必不可少的。

也许我们应该向 Rails 提交一个补丁......

于 2012-10-22T21:43:30.547 回答
7

在 Rails 5 中,您可以使用attribute方法。此方法在此模型上定义具有类型的属性。如果需要,它将覆盖现有属性的类型。

class Thing < ActiveRecord::Base
  attribute :foo, :boolean
end

attribute注意:在从数据库加载的模型上,rails 5.0.0中此功能的行为不正确。因此使用 rails 5.0.1 或更高版本。

于 2017-01-18T17:25:48.643 回答
1

查看validates_acceptance_of代码(单击Show source)。他们通过与“0”进行比较来实现它。

我以这种方式在注册表中使用它:

class User < ActiveRecord::Base
  validates_acceptance_of :terms_of_service
  attr_accessible :terms_of_service
end

如果你真的想从字符串等转换,你可以使用这个:

def foo=(value)
  self.foo=(value == true || value==1 || value =~ (/(true|t|yes|y|1)$/i)) ? true:false
end

或者为类添加类型转换方法String并在模型中使用它:

class String
 def to_bool
   return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
   return false if self == false || self.blank? || self =~ (/(false|f|no|n|0)$/i)
   raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
 end
end
于 2012-07-19T13:29:12.933 回答
0

为什么你不这样做:

def foo=(value)
  @foo = value
  @bar = 'Hello' if value == "1"
end
于 2012-07-19T13:16:17.110 回答