Rails: 使用Scope进行查询
在模型中通过Scope将常用的查询条件定义成方法,可以在关联对象或模型上调用。能帮我们简化代码的书写。
定义简单作用域
class Post < ActiveRecord::Base
scope :published, -> { where(published: true) }
end
上面代码等价于在模型中直接定义类方法,其中的->{}
是lambda函数的简化定义方法
class Post < ActiveRecord::Base
def self.published
where(published: true)
end
end
链式调用
Scope方法支持链式调用,这样我们就能把常用的查询条件组合到一起调用了。
class Product < ActiveRecord::Base
scope :hot, -> { where(hot: true) }
scope :top, -> { where(top: true) }
end
Product.top.hot.where(name: "T-Shirt")
参数传递
class Post < ActiveRecord::Base
scope :created_before, ->(time) { where("created_at < ?", time) }
end
Post.created_before(Time.zone.now)
当有的查询条件需要动态参数的时候,可以采用上面的方法,不过Rails Guides 中推荐如果需要动态参数的时候,不如直接使用类方法,因为有了参数的参与,scope的行为与方法更加接近了,索性不如直接使用方法来定义。
合并Scope
class User < ActiveRecord::Base
scope :active, -> { where state: 'active' }
scope :inactive, -> { where state: 'inactive' }
end
User.active.inactive
# SELECT "users".* FROM "users" WHERE "users"."state" = 'active' AND "users"."state" = 'inactive'
User.active.where(state: 'finished')
# SELECT "users".* FROM "users" WHERE "users"."state" = 'active' AND "users"."state" = 'finished'
通过代码可以看出,将两个scope同时链式使用的时候,其对应的查询条件是以AND进行连接的,此外scope也可以直接拿来与where方法使用。
如果一个地方使用了某个 scope,而要在另一个地方把它的条件改变,可以使用 merge 方法:
class Product < ActiveRecord::Base
scope :active, -> { where state: 'active' }
scope :inactive, -> { where state: 'inactive' }
end
Product.active.merge(User.inactive)
# SELECT "products".* FROM "products" WHERE "products"."state" = 'inactive'