跳至内容 跳至搜索
方法
V

实例公共方法

validates_absence_of(*attr_names)

验证指定的属性不存在(由 Object#present? 定义)。如果属性是关联,则关联的对象也会被视为不存在,如果它被标记为销毁。

有关更多信息,请参见 ActiveModel::Validations::HelperMethods.validates_absence_of

# File activerecord/lib/active_record/validations/absence.rb, line 20
def validates_absence_of(*attr_names)
  validates_with AbsenceValidator, _merge_attributes(attr_names)
end

validates_associated(*attr_names)

验证关联的对象是否全部有效。适用于任何类型的关联。

class Book < ActiveRecord::Base
  has_many :pages
  belongs_to :library

  validates_associated :pages, :library
end

警告:此验证不得在关联的两端同时使用。这样做会导致循环依赖并导致无限递归。

注意:如果关联尚未分配,此验证不会失败。如果你想确保关联既存在又保证有效,你还需要使用 validates_presence_of

配置选项

  • :message - 自定义错误消息(默认值为:“无效”)。

  • :on - 指定此验证处于活动状态的上下文。默认情况下在所有验证上下文中运行 nil。您可以传递一个符号或一个符号数组。(例如 on: :createon: :custom_validation_contexton: [:create, :custom_validation_context]

  • :if - 指定一个方法、过程或字符串来调用以确定是否应执行验证(例如 if: :allow_validationif: Proc.new { |user| user.signup_step > 2 })。该方法、过程或字符串应返回或评估为 truefalse 值。

  • :unless - 指定一个方法、过程或字符串来调用以确定是否不应执行验证(例如 unless: :skip_validationunless: Proc.new { |user| user.signup_step <= 2 })。该方法、过程或字符串应返回或评估为 truefalse 值。

# File activerecord/lib/active_record/validations/associated.rb, line 54
def validates_associated(*attr_names)
  validates_with AssociatedValidator, _merge_attributes(attr_names)
end

validates_length_of(*attr_names)

验证指定的属性是否符合提供的长度限制。如果该属性是一个关联,则不会计算标记为要销毁的记录。

有关更多信息,请参见 ActiveModel::Validations::HelperMethods.validates_length_of

# File activerecord/lib/active_record/validations/length.rb, line 19
def validates_length_of(*attr_names)
  validates_with LengthValidator, _merge_attributes(attr_names)
end

validates_numericality_of(*attr_names)

通过尝试使用 Kernel.Float 将其转换为浮点数(如果 only_integerfalse)或将其应用于正则表达式 /\A[+\-]?\d+\z/(如果 only_integer 设置为 true)来验证指定属性的值是否是数字。Kernel.Float 精度默认为列的精度值或 15。

有关更多信息,请参见 ActiveModel::Validations::HelperMethods.validates_numericality_of

# File activerecord/lib/active_record/validations/numericality.rb, line 31
def validates_numericality_of(*attr_names)
  validates_with NumericalityValidator, _merge_attributes(attr_names)
end

validates_presence_of(*attr_names)

验证指定的属性不为空(由 Object#blank? 定义)。如果属性是关联,则关联对象也会被视为为空,如果它被标记为销毁。

class Person < ActiveRecord::Base
  has_one :face
  validates_presence_of :face
end

face 属性必须在对象中,且不能为空或被标记为销毁。

此验证器推迟到 Active Model 验证以进行存在性验证,并添加检查以查看关联对象是否被标记为销毁。这可以防止父对象成功验证并保存,然后删除关联对象,从而使父对象进入无效状态。

有关更多信息,请参见 ActiveModel::Validations::HelperMethods.validates_presence_of

注意:如果您将此验证与关联一起使用,则在关联被分配但无效时,此验证不会失败。如果您想确保它既存在又有效,您还需要使用 validates_associated

# File activerecord/lib/active_record/validations/presence.rb, line 40
def validates_presence_of(*attr_names)
  validates_with PresenceValidator, _merge_attributes(attr_names)
end

validates_size_of(*attr_names)

validates_uniqueness_of(*attr_names)

验证指定属性的值在整个系统中是否唯一。对于确保只有一个用户可以命名为“davidhh”很有用。

class Person < ActiveRecord::Base
  validates_uniqueness_of :user_name
end

它还可以根据 :scope 参数验证指定属性的值是否唯一

class Person < ActiveRecord::Base
  validates_uniqueness_of :user_name, scope: :account_id
end

甚至多个范围参数。例如,确保一名教师在特定班级中每学期只能在日程表上出现一次。

class TeacherSchedule < ActiveRecord::Base
  validates_uniqueness_of :teacher_id, scope: [:semester_id, :class_id]
end

还可以将唯一性约束限制为一组匹配特定条件的记录。在此示例中,在验证标题属性的唯一性时,不会考虑已归档的文章

class Article < ActiveRecord::Base
  validates_uniqueness_of :title, conditions: -> { where.not(status: 'archived') }
end

要基于记录的状态构建条件,请使用参数定义条件可调用对象,该参数将是记录本身。此示例验证标题对于出版年份是唯一的

class Article < ActiveRecord::Base
  validates_uniqueness_of :title, conditions: ->(article) {
    published_at = article.published_at
    where(published_at: published_at.beginning_of_year..published_at.end_of_year)
  }
end

创建记录时,会执行检查以确保数据库中不存在具有指定属性(映射到列)的给定值的记录。更新记录时,会进行相同的检查,但会忽略记录本身。

配置选项

  • :message - 指定自定义错误消息(默认值为:“已被占用”)。

  • :scope - 一个或多个列,用于限制唯一性约束的范围。

  • :conditions - 指定要作为 WHERE SQL 片段包含的条件,以限制唯一性约束查找(例如 conditions: -> { where(status: 'active') })。

  • :case_sensitive - 查找完全匹配。非文本列忽略。默认行为遵循默认数据库排序规则。

  • :allow_nil - 如果设置为 true,则在属性为 nil 时跳过此验证(默认值为 false)。

  • :allow_blank - 如果设置为 true,则在属性为空时跳过此验证(默认值为 false)。

  • :if - 指定一个方法、过程或字符串来调用以确定是否应执行验证(例如 if: :allow_validationif: Proc.new { |user| user.signup_step > 2 })。该方法、过程或字符串应返回或评估为 truefalse 值。

  • :unless - 指定一个方法、过程或字符串来调用以确定是否不应执行验证(例如 unless: :skip_validationunless: Proc.new { |user| user.signup_step <= 2 })。该方法、过程或字符串应返回或评估为 truefalse 值。

并发性和完整性

将此验证方法与 ActiveRecord::Base#save 结合使用并不能保证没有重复记录插入,因为应用程序级别的唯一性检查本质上容易出现竞争条件。例如,假设有两个用户同时尝试发布评论,并且评论的标题必须是唯一的。在数据库级别,这些用户执行的操作可以按以下方式交错

             User 1                 |               User 2
------------------------------------+--------------------------------------
# User 1 checks whether there's     |
# already a comment with the title  |
# 'My Post'. This is not the case.  |
SELECT * FROM comments              |
WHERE title = 'My Post'             |
                                    |
                                    | # User 2 does the same thing and also
                                    | # infers that their title is unique.
                                    | SELECT * FROM comments
                                    | WHERE title = 'My Post'
                                    |
# User 1 inserts their comment.     |
INSERT INTO comments                |
(title, content) VALUES             |
('My Post', 'hi!')                  |
                                    |
                                    | # User 2 does the same thing.
                                    | INSERT INTO comments
                                    | (title, content) VALUES
                                    | ('My Post', 'hello!')
                                    |
                                    | # ^^^^^^
                                    | # Boom! We now have a duplicate
                                    | # title!

解决此问题的最佳方法是使用 connection.add_index 向数据库表添加唯一索引。在极少数情况下发生竞争条件时,数据库将保证字段的唯一性。

当数据库捕获到此类重复插入时,ActiveRecord::Base#save 将引发 ActiveRecord::StatementInvalid 异常。您可以选择让此错误传播(这将导致显示默认的 Rails 异常页面),或者您可以捕获它并重新启动事务(例如,通过告诉用户标题已存在,并要求他们重新输入标题)。此技术也称为 乐观并发控制

捆绑的 ActiveRecord::ConnectionAdapters 通过抛出 ActiveRecord::RecordNotUnique 异常将唯一索引约束错误与其他类型的数据库错误区分开来。对于其他适配器,您将不得不解析(特定于数据库的)异常消息以检测此类情况。

以下捆绑适配器抛出 ActiveRecord::RecordNotUnique 异常

# File activerecord/lib/active_record/validations/uniqueness.rb, line 286
def validates_uniqueness_of(*attr_names)
  validates_with UniquenessValidator, _merge_attributes(attr_names)
end