Common Validation Options

allow_nil,allow_blank区别

allow_nilallow_blank 都是 Rails 中用于控制属性值是否可以为空的选项,它们之间的区别如下:

  • allow_nil:如果设置了 allow_nil: true,则属性的值可以为 nil,也就是说,在验证时,如果属性的值为 nil,则该属性将被视为合法。需要注意的是,allow_nil 只对 nil 值起作用,对于其他的空值(例如空字符串、空数组等),它是无效的。

  • allow_blank:如果设置了 allow_blank: true,则属性的值可以为空,也就是说,在验证时,如果属性的值为空(例如空字符串、空数组等),则该属性将被视为合法。需要注意的是,allow_blank 对于所有的空值都起作用,包括 nil 值。

因此,如果我们想让一个属性在验证时可以为空,可以使用 allow_nilallow_blank 选项。如果我们只想允许 nil 值为空,可以使用 allow_nil: true;如果我们想允许所有的空值为空,包括 nil 值,可以使用 allow_blank: true

需要注意的是,allow_nilallow_blank 只是控制属性在验证时是否可以为空,它们并不会对数据库中存储的空值进行任何转换。如果我们想让数据库中的空值被转换为默认值,可以使用 Rails 提供的 default 选项来设置默认值。

message

message 选项是 Rails 中用于自定义验证错误消息的选项。当验证失败时,Rails 会生成一条默认的错误消息,例如 "can't be blank""has already been taken"。如果我们想使用自定义的错误消息,可以使用 message 选项来指定。

举个例子,假设我们有一个 Person 模型,其中有一个 name 属性需要进行长度验证,长度必须在 2 到 20 个字符之间。我们可以使用 length 验证器来实现这个验证:

class Person < ApplicationRecord
  validates :name, length: { in: 2..20 }
end

如果在验证时 name 的长度不在 2 到 20 个字符之间,Rails 将会生成一条默认的错误消息,例如 "is too short (minimum is 2 characters)""is too long (maximum is 20 characters)"。如果我们想使用自定义的错误消息,可以使用 message 选项来指定,例如:

class Person < ApplicationRecord
  validates :name, length: { in: 2..20, message: "must be between 2 and 20 characters" }
end

这样,在验证失败时,Rails 将会生成我们指定的错误消息,例如 "Name must be between 2 and 20 characters"

需要注意的是,message 选项可以用于所有的验证器,它可以让我们根据具体的需求和情况来自定义错误消息,以便更好地向用户反馈验证失败的原因。

on

on 选项是 Rails 中用于控制验证器在何时执行的选项。默认情况下,Rails 在保存模型时会执行所有的验证器,如果有任何一个验证器失败,模型就无法被保存。但有时我们可能需要在特定的情况下才执行某些验证器,例如只有在创建模型时才执行某个验证器,或只有在更新模型时才执行另一个验证器。这时就可以使用 on 选项来指定。

on 选项可以接受以下几个值:

  • :create:表示只在创建模型时执行该验证器。
  • :update:表示只在更新模型时执行该验证器。
  • :save:表示在创建或更新模型时都执行该验证器。
  • :validation:表示在调用 valid? 方法时执行该验证器。

举个例子,假设我们有一个 Person 模型,其中有一个 email 属性需要进行唯一性验证。我们希望在创建模型时进行唯一性验证,但在更新模型时不进行唯一性验证。可以使用 on 选项来指定:

class Person < ApplicationRecord
  validates :email, uniqueness: true, on: :create
end

这样,在创建模型时,Rails 将会执行 uniqueness 验证器,但在更新模型时不会执行。

需要注意的是,on 选项可以用于所有的验证器,它可以让我们根据具体的需求和情况来控制验证器的执行时机,以便更好地优化验证器的性能,或者更好地满足业务需求。

Strict Validations

在 Rails 中,strict validations 是一种严格的验证方式,它会在模型保存之前对属性进行类型验证,如果类型不匹配就会抛出异常。

在默认情况下,Rails 中的验证器是比较宽松的,它们会尽可能地将传入的数据转换为正确的类型。例如,如果我们在模型中定义了一个整型属性,但是我们传入了一个字符串类型的值,Rails 会自动将其转换为整型,而不会抛出异常。这种宽松的验证方式可以使得开发更加灵活,但也可能会导致数据类型错误的问题难以被发现。

为了解决这个问题,Rails 引入了 strict validations,它可以让我们在保存模型之前对属性的数据类型进行验证,从而避免数据类型错误的问题。可以通过在模型中设置 config.strict_validation 选项来开启 strict validations

class MyModel < ApplicationRecord
  self.config.strict_validation = true
end

开启 strict validations 后,如果属性的数据类型不匹配,模型在保存时会抛出 ActiveRecord::StrictValidationFailed 异常,从而提醒我们数据类型错误的问题。

需要注意的是,开启 strict validations 可能会对性能产生一定的影响,因为它在保存模型之前需要对属性进行额外的类型验证。因此,我们应该在确保数据类型正确的情况下使用 strict validations,而不是在所有情况下都使用它。

Conditional Validation

Using a Symbol with :if and :unless

在 Rails 中,我们可以在验证器中使用 :if:unless 选项来指定条件,只有当条件满足时才执行验证。其中,:if 选项表示只有当条件为真时才执行验证,而 :unless 选项表示只有当条件为假时才执行验证。

:if:unless 选项可以接受一个方法名或者一个符号作为参数,用来指定要执行的条件方法或者属性。如果我们使用符号作为参数,则 Rails 会将其转换为一个方法名,然后在模型中查找该方法。

例如,假设我们有一个 User 模型,其中有一个 admin 属性,表示该用户是否是管理员。我们希望只对非管理员用户执行某些验证,可以在验证器中使用 :unless 选项来指定条件:

class User < ApplicationRecord
  validates :email, presence: true, unless: :admin?

  def admin?
    self.admin
  end
end

在上面的例子中,我们在 validates 方法中使用了 :unless 选项,并传入了一个 admin? 方法的符号。这表示只有当 admin? 方法返回 false 时才会执行邮箱属性的验证。在 admin? 方法中,我们检查 admin 属性是否为 true,如果是则表示该用户是管理员,返回 true,否则返回 false

需要注意的是,使用符号作为 :if:unless 选项的参数时,Rails 会在模型中查找对应的方法。因此,我们需要确保在模型中定义了该方法,并且该方法返回一个布尔值,以便正确地执行验证。

Using a Proc with with :if and :unless

在 Rails 中,我们可以在验证器中使用 :if:unless 选项来指定条件,只有当条件满足时才执行验证。其中,:if 选项表示只有当条件为真时才执行验证,而 :unless 选项表示只有当条件为假时才执行验证。

除了使用方法名或者符号作为条件外,我们还可以使用 Proc 对象,来指定一个匿名方法作为验证的条件。例如,假设我们有一个 User 模型,其中有一个 role 属性,表示该用户的角色。我们希望只对管理员用户执行某些验证,可以在验证器中使用 :if 选项来指定条件:

class User < ApplicationRecord
  validates :email, presence: true, if: Proc.new { |user| user.role == 'admin' }
end

在上面的例子中,我们在 validates 方法中使用了 :if 选项,并传入了一个匿名方法。这个匿名方法接受一个 User 对象作为参数,并检查该用户的角色是否为 admin。只有当角色为 admin 时才会执行邮箱属性的验证。

需要注意的是,使用 Proc 对象作为 :if:unless 选项的参数时,我们需要确保该 Proc 对象返回一个布尔值,以便正确地执行验证。在 Proc 对象中可以使用当前模型对象的任何属性或方法,因此可以实现非常复杂的条件判断。但是,由于 Proc 对象是在运行时动态创建的,因此可能会对性能产生一定的影响,特别是在处理大量记录时。

Grouping Conditional validations

在 Rails 中,我们可以使用 :if:unless 选项来指定条件,只有当条件满足时才执行验证。有时候,我们需要同时使用多个条件来控制验证器的行为。例如,假设我们有一个 User 模型,其中有一个 country 属性,表示该用户所在的国家。我们希望对美国用户执行一组验证,对其他国家的用户执行另一组验证。可以在验证器中使用 ifunless 选项来实现:

class User < ApplicationRecord
  with_options if: Proc.new { |user| user.country == 'USA' } do |usa_user|
    usa_user.validates :ssn, presence: true
    usa_user.validates :state, presence: true
  end

  with_options unless: Proc.new { |user| user.country == 'USA' } do |non_usa_user|
    non_usa_user.validates :passport_number, presence: true
    non_usa_user.validates :city, presence: true
  end
end

在上面的例子中,我们使用 with_options 方法来分组验证器。在第一个分组中,我们使用 if 选项来指定条件,只有当用户所在的国家为美国时才会执行验证。在第二个分组中,我们使用 unless 选项来指定条件,只有当用户所在的国家不是美国时才会执行验证。在每个分组中,我们使用 validates 方法来指定要验证的属性和验证规则。

需要注意的是,在 with_options 块中使用多个验证器时,它们都会继承该块中指定的条件。因此,在上面的例子中,usa_user 分组中的两个验证器都会继承 if 选项中指定的条件,即只对美国用户进行验证。同样地,non_usa_user 分组中的两个验证器都会继承 unless 选项中指定的条件,即只对非美国用户进行验证。

使用分组验证器可以让我们更清晰地组织验证规则,使代码更易于维护和扩展。同时,分组验证器也可以提高验证器的性能,因为它们只会对符合条件的记录进行验证。

Performing Custom Validations

在 Rails 中,我们可以使用验证器来验证模型对象的属性。有时候,我们需要执行一些自定义的验证,这时可以定义一个自定义验证器。自定义验证器是一个普通的 Ruby 类,它实现了 validate 方法,可以在其中执行任意的验证逻辑。

例如,假设我们有一个 Product 模型,其中有一个 price 属性,表示该商品的价格。我们希望对价格进行一些自定义的验证,例如价格不能为负数或者价格不能超过 1000 元。可以定义一个自定义验证器来实现:

class PriceValidator < ActiveModel::Validator
  def validate(record)
    if record.price < 0
      record.errors.add :price, '不能为负数'
    elsif record.price > 1000
      record.errors.add :price, '不能超过 1000 元'
    end
  end
end

class Product < ApplicationRecord
  validates_with PriceValidator
end

在上面的例子中,我们定义了一个 PriceValidator 类,它实现了 validate 方法。在 validate 方法中,我们检查价格是否为负数或者是否超过 1000 元,如果是,则将错误信息添加到模型对象的错误列表中。在 Product 模型中,我们使用 validates_with 方法来指定要使用的验证器。

需要注意的是,自定义验证器必须实现 validate 方法,并且接受一个模型对象作为参数。在 validate 方法中,我们可以使用模型对象的任何属性或方法来执行验证逻辑,并且可以使用 record.errors.add 方法将错误信息添加到模型对象的错误列表中。

使用自定义验证器可以让我们更灵活地控制验证器的行为,可以实现任意复杂的验证逻辑。同时,自定义验证器也可以提高代码的可读性和可维护性,使代码更易于理解和修改。

posted @   卓亦苇  阅读(24)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类
点击右上角即可分享
微信分享提示