最近在错误系统中突然发现了一个undefined method 'first_tags' for #<Wisdom::Tag::ActiveRecord_Relation:...>的错误,其中first_tagswisdom_tags这张表中的一个字段,开始以为是其中使用的scope的语法问题,但是仔细探究下来发现并不是,是自己以前没有注意的问题,所以记录一下。

首先在model层存在一个scope,代码如下:

1
2
3
4
5
class Wisdom::Tag < ApplicationRecord
  belongs_to :evaluation, :class_name => 'Wisdom::Evaluation', foreign_key: 'evaluation_id'
    
  scope :evaluation_tag, ->(evaluation) { where(week_index: 0, evaluation: evaluation).last }
end

在正常的业务情况下,每一条wisdom_evaluation都有对应的wisdom_tags,因此这个scope在业务中也是没有问题的。但是由于特殊情况,存在一条评测数据其对应的标签数据没有成功创建,因此这个scope在使用时没有找到对应的数据,而返回了一个包含全部wisdom_tag数据的ActiveRecord::Relation,这也就导致了后续方法在调用时产生报错。

Adds a class method for retrieving and querying objects. The method is intended to return an ActiveRecord::Relation object, which is composable with other scopes. If it returns nil or false, an all scope is returned instead.

官方对应的文档上其实也有说明,另外这里stackoverflow有个不错的回答。 scope返回nil或者false时,所有的记录会被返回,这个设计是有意为之,是为了scopes的链式调用,这一点也是和class_method的关键区别,具体的例子可以去提供的回答中看一下,写的挺详细的,这一点也是自己以前没有注意到的。

所以针对这种情况,当你需要firstlast或者find这类的返回具体的数据的时候,优先考虑使用class_method来替代使用scope,避免出现类似的bug哈。