我有一个带有一些属性和虚拟属性的模型。 此虚拟属性用于在创建表单中创建一个复选框。
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
这里的问题是,即使foo
为'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
但我觉得这样做很脏。没有重写转换方法,有没有更好的选择?
由于
答案 0 :(得分:15)
原始帖子中的解决方案对我来说是最好的解决方案。
class Thing < ActiveRecord::Base
attr_reader :foo
def foo=(value)
@foo = ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value)
end
end
如果您想稍微清理一下,可以随时创建一个帮助方法,使用foo=
为您定义value_to_boolean
编写器方法。
我可能会创建一个名为bool_attr_accessor
的方法的模块,因此您可以将模型简化为:
class Thing < ActiveRecord::Base
bool_attr_accessor :foo
end
似乎ActiveModel应该为我们提供这样的东西,因此虚拟属性更像是&#34;真实的&#34; (ActiveRecord-persisted)属性。只要您具有从表单提交的布尔虚拟属性,此类型转换就非常重要。
也许我们应该向Rails提交补丁......
答案 1 :(得分:3)
在Rails 5中,您可以使用attribute
方法。此方法定义此模型上具有类型的属性。如果需要,它将覆盖现有属性的类型。
class Thing < ActiveRecord::Base
attribute :foo, :boolean
end
警告:从db 5.0加载的模型中,rails 5.0.0中此attribute
功能的行为不正确。因此,请使用rails 5.0.1或更高版本。
答案 2 :(得分:1)
查看validates_acceptance_of
代码(点击显示来源)。
他们通过比较“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
答案 3 :(得分:0)
为什么你不这样做:
def foo=(value)
@foo = value
@bar = 'Hello' if value == "1"
end