如何强制rails ActiveRecord字符串列只接受字符串?

时间:2015-07-14 16:12:43

标签: ruby-on-rails activerecord

我有一个Post类,其中title:string字段

post = Post.create!
#=> Post created
post.title = "foo"
post.save! 
# success
post.title = 1
post.save! 
# succeeds, but I want it to throw a type mismatch exception

如何获得最后一次保存!抛出类型不匹配?

5 个答案:

答案 0 :(得分:3)

这实际上违背了Ruby的全部内容。如果它是一个字符串列,数据库层将负责将该值呈现为字符串并保存它。

Ruby中的类型概念与其他语言不同。通常,如果一个对象支持to_s,那么它被认为是 string-enough ,这是Duck Typing的基本原则。

如果传入无法呈现为字符串的内容,则会出现异常。例如:

post.title = Post.find(5)

如果您希望确保字段不是数字,则应添加强制执行特定格式的验证。

答案 1 :(得分:2)

已经创建了一个新的gem来帮助validate types in railsexplanatory blog post来回答更多因为它首先创建的“原因”。

使用此库,您的代码很简单:

cat /media/DRIVENAME/BACKUPNAME.tgz.* | tar -x /

当将整数分配给class Post < ActiveRecord::Base validates_type :title, :string end 而不是将title静默地转换为字符串并保存它时,这将抛出异常。

答案 2 :(得分:1)

Ruby是一种鸭子类型的语言,没有类型不匹配。如果对象响应to_s,Rails将在其上调用to_s并将其放入字符串字段中。

听起来你真正想要的是验证你放入字段的值的格式(字符串"2"是一个有效值?),这是validates_format_of的用途。例如,如果要确保该值至少包含一个非数字字符,则可以执行以下操作:

class Post < ActiveRecord::Base
  validates_format_of :title, with: /\D/
end

当然,您可能希望更多地自定义该正则表达式。

如果你真的无法忍受鸭子打字并想确保除了真正的字符串进入该列之外什么都没有,你可以覆盖title=

class Post < ActiveRecord::Base
  def title=(value)
    throw "`title' must be a String" unless value.is_a?(String)
    super
  end
end

请注意,执行此操作时不会调用此方法。 post[:title] = ...post.write_attribute(:title, ...),但它应涵盖95%的情况。

答案 3 :(得分:0)

要回答确切的问题,如果要确保只将类String的对象保存到字段中,则应添加以下自定义验证。

  validate :must_be_string

  def must_be_string
    errors.add(:base, 'The field must be String') unless title.class == String
  end

答案 4 :(得分:0)

您可以定义自定义验证程序。

validates :title, type: String

然后:

class TypeValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    record.errors.add attribute, (options[:message] || "is not of class #{options[:with]}") unless
      value.class == options[:with]
  end
end