如何验证一个字段是空的还是数字?

问题描述 投票:0回答:1

我想验证接收到的参数是空字符串还是正数。我试过:

validates :my_field,
  presence: true,
  if: ->{
    ((my_field.is_a? String) && my_field.empty?) ||
    ((my_field.is_a? Numeric) && my_field > 0)
  }

但它不起作用。我仍然可以放置非空字符串和负值。我该怎么办?

ruby string validation ruby-on-rails-3 numeric
1个回答
0
投票

TL;博士

Ruby on Rails 3 真的 旧,虽然您可能会得到适用于 Rails 3 的答案,但它早已过时,运行它的 Ruby 版本也是如此。我的回答将假定 Ruby 3.2.2 和 Rails 7.0.4.2,但您当然可以将其适应旧版本,只要您想要的功能得到支持或愿意推出您自己的兼容实现即可。

前言:用户输入始终是一个字符串

在 Ruby on Rails 中,您使用的那种 ActiveRecord 验证 通常是模型问题。然而,来自 HTML 表单的用户输入始终是一个字符串,除非你的模型、视图或控制器在将它传递到应用程序的其他部分之前强制它。

您的应用程序中有很多不同的地方可以放置您的逻辑,但最佳实践通常是将此类逻辑放入模型中。 ActiveRecord 会为您处理很多强制转换,这使它成为这里最简单和最可靠的解决方案。可能也有理由在 MVC 堆栈的其他地方进行额外的验证或强制转换,所以我也会尝试至少对这些选项进行一些简单的介绍。

最佳实践:模型验证

模型:使用 ActiveRecord 验证

如果您遵循“胖模型,瘦控制器”的历史最佳实践,那么您可以在模型中进行一项或多项验证。一个示例可能包括运行多个验证。您可以通过每行一个变量验证来执行此操作,或者在同一行上使用一系列验证。

请注意,许多验证还接受关键字参数的哈希以进行额外检查。考虑以下内容,它在最新版本的 Rails 中使用 :allow_blank:numericality 验证。

class Foo < ApplicationRecord
  validates :my_field,
    allow_blank: true,
    numericality: {
      only_integer: true,                                                       
      greater_than_or_equal_to: 1
    }   
end

你可以像这样在

rails console
内测试这个:

Foo.create(my_field: "").save!
#=> true

Foo.create(my_field: nil).save!
#=> true

Foo.create(my_field: "1").save!
#=> true

边栏:为您的数据库适当地处理 Nil 值

:allow_blank 验证除了接受空字符串外,还接受文字

nil
值。某些数据库(例如 SQLite)不会为您将 nil 强制转换为 String;结果可能因其他数据库而异,但如果字段的类型定义不允许
null
.

,许多数据库会引发异常

如果您想确保不以与数据库无关的方式存储字面 nil 值,那么您需要通过在上面的验证中将 :allow_nil 哈希值设置为

false
来排除 nils,或者添加一个 #before_save 回调 在将它保存到数据库之前将 nil 强制转换为适当的字符串值。


使用 bang 方法,失败的验证将引发:

Validation failed: My field is not a number (ActiveRecord::RecordInvalid)

您可以调整各种验证并根据需要更改返回的消息或引发的异常,但这可能足以让您继续前进,并且足以满足您的即时需求。

其他选项

View:在表单上使用约束

你可以在你的观点或部分做这样的事情:

<input type="number" name="my_field" min="1"/>

这将确保不能以小于一的形式输入值。您可以执行许多其他 HTML 和 JavaScript 验证,但确保用户不能首先输入负值或非整数值是一个好的开始。当控制器得到响应时它仍然是一个字符串,但至少它已经是可强制格式了。

控制器:将字符串强制转换为整数

在您的控制器中,您还可以获取用户返回的值并在对值调用 ActiveRecord 方法之前强制它们。我不是 100% 确定最新版本的 Rails 不会根据字段类型自动为您执行某些操作,但如果它们不这样做,您可以简单地使用一些非常基本的 Ruby。例如:

class MyController < ApplicationController
  def create
    # Coerce the value, even if #blank?, to an
    # Integer. Note that if the param is #blank?
    # then #to_i will return +0+. #abs ensures
    # the value is positive.
    @my_field = params[:my_field].to_i.abs
  end

这种方法不能保证用户输入有效值。它只是确保该值是零或正整数。您 could 还可以确保它永远不会小于最小值,例如:

@my_field = params[:my_field].to_i.abs
@my_field = @my_field.nonzero? ? @my_field : @my_field.succ

这解决了这两个问题,但实际上并没有阻止用户输入无效数据或根本不输入任何内容。它只是确保变量在调用任何模型方法之前保存正确类型的数据。

© www.soinside.com 2019 - 2024. All rights reserved.