如何使用Rails关联作用域中定义的Proc来自动生成作用域
解决方案
你拿到的关联scope对应的Proc,需要在关联目标类的上下文中执行,再合并到关联查询逻辑中即可生成对应的作用域。
核心用法
关联反射的scope属性存储的Proc,执行时的上下文是关联的目标类(比如示例中的Author类),可以通过instance_exec方法在目标类上下文调用这个Proc,再把生成的查询条件合并到主模型的关联查询中:
# 1. 拿到关联反射 author_reflection = Book.reflections["author"] # 2. 在关联目标类上下文执行Proc,得到目标类的查询条件 active_author_scope = author_reflection.klass.instance_exec(&author_reflection.scope) # 3. 合并到Book的关联查询中,生成对应作用域 scope :with_active_author, -> { joins(:author).merge(active_author_scope) }
自动生成作用域的通用实现
你可以在gem中通过遍历模型所有关联反射,批量生成对应作用域,参考实现如下:
module AutoScopeGenerator extend ActiveSupport::Concern included do # 遍历当前模型所有关联 reflections.each do |association_name, reflection| # 只处理定义了scope的关联 next unless reflection.scope # 自动生成 with_xxx 作用域 scope "with_#{association_name}", -> do joins(association_name).merge( # 关联目标类上下文执行scope Proc reflection.klass.instance_exec(&reflection.scope) ) end # 也可以按需生成 left_joins 版本的作用域 scope "left_join_#{association_name}", -> do left_joins(association_name).merge( reflection.klass.instance_exec(&reflection.scope) ) end end end end # 在模型中引入即可自动生成对应作用域 class Book < ApplicationRecord belongs_to :author, -> { where active: true } include AutoScopeGenerator end
效果验证
引入实现后,即可直接调用生成的作用域查询:
# 仅返回关联了启用状态作者的图书 Book.with_author # 包含未关联作者、关联了启用状态作者的所有图书 Book.left_join_author
注意事项
- 部分关联scope会接收所属模型实例作为参数(比如
belongs_to :author, ->(book) { where("created_at > ?", book.published_at) }),这类scope适用于实例级关联查询,生成类级别作用域时需要额外处理参数传递逻辑。 - 该逻辑对
has_many、has_one等其他关联类型通用,不需要单独适配。
内容的提问来源于stack exchange,提问作者datae
相关产品推荐
相关产品推荐

