弃用警告:危险查询方法:ActiveRecord中的随机记录> = 5.2

Dan*_*iel 23 ruby-on-rails rails-activerecord deprecation-warning ruby-on-rails-5.2

到目前为止,从数据库获取随机记录的"常见"方法是:

# Postgress
Model.order("RANDOM()").first 

# MySQL
Model.order("RAND()").first
Run Code Online (Sandbox Code Playgroud)

但是,在Rails 5.2中执行此操作时,它会显示以下Deprecation警告:

DEPRECATION WARNING:使用非属性参数调用的危险查询方法(其参数用作原始SQL的方法):"RANDOM()".Rails 6.0中不允许使用非属性参数.不应使用用户提供的值调用此方法,例如请求参数或模型属性.可以通过将它们包装在Arel.sql()中来传递已知安全值.

我对Arel并不熟悉,所以我不确定解决这个问题的正确方法是什么.

mu *_*ort 35

如果你想继续使用,order by random()那么只需通过将其包装起来就可以将其声明为安全,Arel.sql就像弃用警告建议的那样:

Model.order(Arel.sql('random()')).first
Run Code Online (Sandbox Code Playgroud)

有很多方法可以选择一个随机行,它们都有优点和缺点,但有时候你绝对必须使用一段SQL order by(例如当你需要命令匹配一个Ruby数组并且必须得到一个大case when ... end表达到数据库)所以使用Arel.sql来解决这个"仅属性"限制是我们都需要了解的工具.

编辑:示例代码缺少右括号.

  • 我喜欢这少多少代码。现在我必须告诉AR我正在使用sql。 (2认同)

Ant*_* L 5

我是这个解决方案的粉丝:

Model.offset(rand(Model.count)).first
Run Code Online (Sandbox Code Playgroud)

  • @Daniel 但公平地说,如果使用大型结果集,`order by random()` 可能会非常昂贵。 (6认同)
  • 说得通。我看到这个解决方案的唯一问题是它两次击中数据库,而不是一次,并且理论上它可能会由于竞争条件而失败。 (5认同)
  • 请注意,对于大型 postgres 表,Model.count 可能需要*长时间*。 (3认同)