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

9vw9lbht  于 2023-04-11  发布在  Ruby
关注(0)|答案(1)|浏览(119)

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

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

但是它不起作用。我仍然可以输入非空字符串和负值。我应该怎么做呢?

yqlxgs2m

yqlxgs2m1#

TL;DR

Ruby on Rails 3已经很老了,虽然你可能会得到一个在Rails 3上工作的答案,但它已经过了生命周期的尽头,它运行的Ruby版本也是如此。我的答案将假设Ruby 3.2.2和Rails7.0.4.2,但你当然可以在你想要的功能被支持或愿意推出自己的兼容实现的范围内将其适应旧版本。

前言:用户输入始终为字符串

在Ruby on Rails中,您正在使用的ActiveRecord validations类型通常是一个模型问题。然而,来自HTML表单的用户输入始终是一个String,除非您的模型,视图或控制器在将其传递到应用程序的其他部分之前强制它。
在你的应用程序中有很多不同的地方你可以放置你的逻辑,但是最好的做法通常是将这样的逻辑放在模型中。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
侧边栏:为您的数据库适当地处理零值

allow_blank验证也接受一个文本nil值和一个空的String。一些数据库,如SQLite不会将nil强制转换为String;结果可能因其他数据库而异,尽管如果字段的类型定义不允许null,许多数据库会引发异常。
如果你想确保你不以数据库不可知的方式存储一个nil值,那么你需要通过在上面的验证中将:allow_nil Hash值设置为false来排除nils,或者添加一个#before_保存回调来将nil强制转换为一个合适的String值,然后再将其保存到数据库中。
使用bang方法,失败的验证将引发:
验证失败:我的字段不是数字(ActiveRecord::RecordInvalid)
您可以调整各种验证,并在需要时对返回的消息或引发的异常进行更改,但很可能这足以让您继续下去,并满足您的即时需求。

其他选项

视图:表单使用约束

你可以在你的视图或部分视图中这样做:

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

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

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

在你的控制器中,你也可以获取用户返回的值,并在对这些值调用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

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

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

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

相关问题